feat: implement doctor data migration and update booking link UI in doctor-detail template

This commit is contained in:
2026-07-29 07:55:55 +07:00
parent 186f70ec32
commit 9bfd3ba5f9
51 changed files with 1070681 additions and 497 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

+1
View File
@@ -0,0 +1 @@
SELECT d.name, s.name as specialty FROM sis_doctor d LEFT JOIN sis_specialty s ON d.specialty_id = s.id WHERE d.name = 'Trương Lê Anh Kiệt';
+42
View File
@@ -0,0 +1,42 @@
import csv
import xml.etree.ElementTree as ET
import sys
import os
csv_file = '/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/DS_BacSi/DANH SÁCH BÁC SĨ NGỒI PHÒNG KHÁM_THÔNG TIN CHI TIẾT_FULL.csv'
xml_file = '/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/DS_BacSi/bnhvinakhoaquctsiscnth.WordPress.2026-07-28 (5).xml'
print("CSV Headers:")
with open(csv_file, 'r', encoding='utf-8-sig') as f:
reader = csv.reader(f)
print(next(reader))
print("\nXML meta keys for first doctor:")
tree = ET.parse(xml_file)
root = tree.getroot()
ns = {
'wp': 'http://wordpress.org/export/1.2/',
'content': 'http://purl.org/rss/1.0/modules/content/'
}
for channel in root.findall('channel'):
for item in channel.findall('item'):
post_type = item.find('wp:post_type', ns)
if post_type is not None and post_type.text == 'doctor':
print('Title:', item.find('title').text)
for meta in item.findall('wp:postmeta', ns):
key = meta.find('wp:meta_key', ns)
val = meta.find('wp:meta_value', ns)
if key is not None and val is not None and not key.text.startswith('_'):
print(f" {key.text}")
break
# Count doctors in each
print(f"\nTotal items in XML:")
c = 0
for channel in root.findall('channel'):
for item in channel.findall('item'):
post_type = item.find('wp:post_type', ns)
if post_type is not None and post_type.text == 'doctor':
c += 1
print(c)
+1
View File
@@ -0,0 +1 @@
SELECT table_name, column_name, data_default FROM user_tab_columns WHERE table_name = 'SIS_SPECIALTY';
View File
+69
View File
@@ -0,0 +1,69 @@
Name,Position,Gender,Birthday,Phone,Email,Address,Order,ImageID,ImageURL,Content
Th.S HOÀNG KIM NGA,Phó giám đốc tài chính,nữ,20181003,123,bbb@gmail.com,fdfdfdfd,2,5394,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Thạc sĩ Tài chính - Ngân hàng năm 2010 tại CFVG TP.HCM  (Liên kết giữa ĐH Kinh tế và Đại Học EFCG Pháp) Tốt nghiệp Cử nhân Anh văn năm 1997 tại Đại họ
TS.BS Tăng Hà Nam Anh,Cố vấn chuyên môn,nam,20181002,012455221,bbb@gmail.com,adfsda,1,1713,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Tiến Sĩ Y Khoa chuyên ngành Chấn Thương Chỉnh Hình tại Trường Đại Học Y Dược TP. Hồ Chí Minh Kinh nghiệm làm việc: Trưởng Khoa Chấn Thương Chỉnh Hì
TS.BS TRẦN CHÍ CƯỜNG,Giám đốc,nam,20181025,,,,0,12940,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y khoa năm 2000 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa 1 chuyên ngành Phẫu thuật Thần kinh (Ngoại Thần kinh) năm 200
BS.CKII PHAN TRỊNH MINH HIẾU,Phó giám đốc điều hành,nữ,20181011,0123456789,abc@gmail.com,abc,1,3542,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Đại học Y Khoa năm 2000 tại Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Sản Phụ Khoa năm 2004 tại Đại học Y Dược TP. Hồ Chí Mi
DS. Lê Hoàng Mai,Trưởng khoa,nữ,19751016,,,,1,4589,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Dược Sĩ Đại Học năm 1972 tại Trường Đại Học Dược Khoa Sài Gòn Chứng chỉ đào tạo chuyên môn Dinh Dưỡng năm 1990 của Đại Học Mahidol - Thái Lan Chứng
ĐD. Nguyễn Thị Thiên Trang,Điều dưỡng trưởng,nữ,19800207,,,,4,2249,,Giới tính: Nữ Quá trình đào tạo: Chứng chỉ đào tạo Điều dưỡng năm 1998 của TTĐT & BDCBYT - TP. Hồ Chí Minh Chứng chỉ đào tạo Năng lực quản lý cấp trung năm 2017 của FBT School of Business
BS.CKII HUỲNH QUỐC SĨ,Trưởng khoa,nam,20180405,,,,0,4570,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2009 tại Trường Đại học Y Dược Cần Thơ Tốt nghiệp Thạc sĩ chuyên ngành Nội Tổng quát năm 2015 tại Trường Đại học Y Dược Cần Thơ
BS.CKI Nguyễn Thị Hạnh,Phó khoa,nữ,19790413,,,,1,4552,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2005 tại Trường Đại học Y Dược Hải Phòng Tốt nghiệp Bác sĩ Chuyên khoa I năm 2014 tại Trường Đại học Y Dược TP.HCM Kinh nghiệm làm
ĐD. Nguyễn Thị Thủy Tiên,Điều dưỡng trưởng,nữ,20181101,0123456789,abc@gmail.com,fdfdfdfd,1,8010,,Giới tính: Nữ Quá trình đào tạo: Chứng chỉ đào tạo Điều dưỡng đa khoa năm 2016 của Trường Cao Đẳng Y Tế Tiền Giang Cử nhân: Điều dưỡng Trường Đại Học Cửu Long (năm 2021) Chứng chỉ: Quản lí đ
Đinh Hoàng Khoa,Trưởng phòng,nam,20181205,,,,0,,,Trưởng phòng
Nguyễn Văn Chiến,Nhân viên,nam,19991214,,,,1,,,Nhân viên
Phan Minh Quang,Nhân viên,nam,20011211,,,,2,,,Nhân viên
BS.CKI PHƯƠNG HỒNG THỌ,Phó khoa,nam,20181220,,,,1,4588,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 1999 tại Trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Nội Thần kinh năm 2015 tại Trường Đại học Y Dư
ThS.BS NGUYỄN ANH TRUNG,Phó khoa,nam,20181220,,,,0,4587,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Thạc sĩ chuyên ngành Chấn thương Chỉnh hình tại Trường Đại học Y Dược TP.HCM Chứng chỉ đào tạo Thay khớp của Trường Đại học Y Dược TP.HCM Chứng chỉ
BS.CKI NGÔ THỊ NHƯ THÙY,Nhân viên,nữ,20181220,,,,2,1133,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác Sĩ Đa Khoa năm 2000 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Nhi Khoa năm 2009 tại Trường Đại Học Y Dược TP.
ĐD. Lê Quang Khải,Điều dưỡng trưởng,nam,20190301,,,,1,4586,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Trung cấp Điều dưỡng Đa khoa năm 2010 tại Trường Trung cấp Y tế Vĩnh Long Chứng chỉ đào tạo liên tục - Chứng chỉ CRRT năm 2014 của Bệnh viện 115 TP.
ĐD. Đặng Thị Ngọc Trân,Điều dưỡng trưởng,nữ,20190301,,,,1,1854,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Trung cấp Điều dưỡng đa khoa năm 2006 tại Trường trung học y tế Bạc Liêu Tốt nghiệp Cao đẳng Điều dưỡng đa khoa năm 2018 tại Trường Cao đẳng Y tế Cần
ĐD. Trương Thị Ngọc Linh,Điều dưỡng trưởng,nữ,20190301,,,,1,4585,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Trung cấp Điều dưỡng đa khoa năm 2009 tại Trường Cao đẳng Y tế Cần Thơ Chứng chỉ Điều dưỡng năm 2012 tại Bệnh Viện Đa Khoa Hoàn Mỹ Cửu Long Kinh nghi
ĐD. Trần Việt Khanh,Điều dưỡng trưởng,nam,20190301,,,,1,4583,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Cử nhân Điều dưỡng đa khoa năm 2011 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Cử nhân Ngôn ngữ Anh năm 2016 tại Trường Đại Học Cần Thơ Chứng chỉ
BS.CKII LÊ UYỂN DIỄM,Trưởng khoa,nữ,20190301,,,,0,1863,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 1996 tại Trường Đại Học Y Dược Huế Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Chẩn đoán hình ảnh năm 2005 tại Trường Đại Học Y Dư
TS.BS NGUYỄN HUY THẮNG,Cố vấn chuyên môn,nam,20190301,0955555555,hh@gmail.com,Can tho,1,1865,,Giới tính: Nam Bằng cấp chuyên môn: Tốt nghiệp trường Đại học Y dược TP Hồ Chí Minh năm 1993 Bằng Tiến sĩ - Trường Đại học Y dược TP Hồ Chí Minh năm 2012 (Tiến sĩ chuyên ngành Thần kinh) Đào
BS.CKI TRẦN ÁI THANH,Phó khoa,nữ,20190521,,,,1,4563,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác Sĩ Đa Khoa năm 2008 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Nội - Tổng quát năm 2016 tại Trường Đại Học Y D
BS.CKI TÔ VĂN TÂN,Phó khoa,nam,20190522,,,,1,4582,,Giới tính: Nam Quá trình đào tạo:  Tốt nghiệp Bác Sĩ Đa Khoa năm 2011 tại Trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa Cấp I Nội Tổng quát năm 2018 tại Trường Đại học Y Dược Cần T
BS.CKII BÙI THỊ BÍCH,Phó khoa,nữ,20190924,,,,1,4571,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Chẩn đoán hình ảnh năm 2011 tại Trường Đại Học Y Dược TP. Hồ Chí Minh Tốt nghiệp Bác Sĩ Chuyên Khoa II chuyên ngành
ThS.BS NGUYỄN MINH NGUYỆT,Nhân viên,nam,20200714,,,,0,4581,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2007 tại Trường Đại Học Y Dược TP. Cần Thơ Tốt nghiệp Thạc sĩ Nội tổng quát năm 2014 tại Trường Đại Học Y Dược TP. Hồ Chí Minh Ch
BS.CKII CHÂU THỊ THÚY LIỄU,Trưởng khoa,nữ,19800101,2,aa@gmail.com,,1,5453,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 1998 tại Trường Đại học Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Nội thần kinh năm 2006 tại Trường Đại Học Y Dược TP. H
BS NGUYỄN ĐÀO NHẬT HUY,Nhân viên,nam,20200724,,,,0,4561,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2012 tại trường Đại học Cần Thơ Chứng nhận Kiến tập Tim mạch Can thiệp năm 2013 tại Bệnh viện Chợ Rẫy Chứng nhận Siêu âm Tim năm 2019
ThS.BS TRẦN MINH LUẬN,Nhân viên,nam,20200724,,,,0,3153,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2010 tại trường Đại học Cần Thơ Tốt nghiệp Thạc sĩ Thần kinh - Tâm thần năm 2016 tại trường Đại học Y Dược TP.HCM Chứng nhận Chup - C
BS. TÔ THANH ỬNG,Nhân viên,nam,20200724,,,,0,4576,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2012 tại trường Đại học Y Dược Cần Thơ Chứng nhận Siêu âm tổng quát năm 2014 tại trường Đại học Y Dược Cần Thơ Chứng nhận Nội soi Phế
ThS.BS NGUYỄN LƯU GIANG,Trưởng khoa,nam,20200724,,,,0,4580,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2011 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Thạc sĩ Y học năm 2016 tại trường Đại học Y Dược Cần Thơ Chứng chỉ Định hướng chẩn
ThS.BS NGUYỄN MẠNH CƯỜNG,Trưởng khoa,nam,20200724,,,,0,4562,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2011 trường Đại học Y dược Cần Thơ Tốt nghiệp Thạc Sĩ Y học chuyên ngành Nội khoa năm 2016 tại trường Đại học Y dược Cần Thơ Giấ
ThS.BS TRƯƠNG PHẠM VĨNH LỄ,Trưởng phòng,nam,19800101,,,,3,4579,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2011 tại trường Đại học Cần Thơ Tốt nghiệp Thạc sĩ Thần kinh năm 2016 tại trường Đại học Y Dược Tp.HCM Chứng nhận Siêu âm tổng quát n
ThS.BS TRẦM THỊ KIM SA,Nhân viên,nam,20210705,,,,0,4578,,Giới tính: Nữ Quá trình đào tạo: ● Tốt nghiệp Y đa khoa năm 2000 tại trường Đại học Cần Thơ ● Tốt nghiệp Thạc sĩ Nội khoa năm 2007 tại trường Đại học Y Dược Cần Thơ ● Chứng nhận Đào tạo Hồi sức cấp
BS. TRẦN TIẾN THÀNH,Nhân viên,nam,20210705,,,,0,4623,,Giới tính: Nam Quá trình đào tạo: 2010 - 2016: Bác sĩ Đa Khoa  Đại Học Y Dược Cần Thơ 2019 - 2021: Đào tạo BS.CKI Nội Thần Kinh  - Đại Học Y Dược Tp HCM. Chứng nhận cập nhật chẩn đoán điều trị
BS. DƯƠNG HOÀNG LINH,Nhân viên,nam,20210705,,,,0,4575,,"Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y Dược Cần Thơ năm 2017 Kinh nghiệm làm việc: 7/2017-9/2020: BS Khoa hồi sức cấp cứu - chống độc Trung tâm y tế huyện Gò Quao, tỉnh Kiên Gi"
BS.CKI PHAN THỊ THU,Nhân viên,nam,20210706,,,,0,,,Giới tính: Nữ Quá trình đào tạo: 1997 : Tốt nghiệp Đại Học Y Dược TP Hồ Chí Minh 2008 : Tốt nghiệp CKI Nội Thần Kinh Đại Học Y Dược TP. Hồ Chí Minh Kinh nghiệm làm việc: Bác sĩ Khoa Nội tổng
ThS.BS NGUYỄN HẢI NGUYÊN,Nhân viên,nam,20210705,,,,0,4565,,Giới tính: Nam Quá trình đào tạo: 2012 Tốt nghiệp Bác sĩ Đa Khoa Đại Học Y Dược Cần Thơ Kinh nghiệm làm việc: 2012-2015 Bác sĩ nội khoa Bệnh viện Đa Học Y Dược Cần Thơ 2015-2018 Bác sĩ nội k
ThS.BS LƯU TRÚC PHƯƠNG,Phó khoa,nam,20210706,0358032332,drphuongluu@dotquy.vn,,1,9459,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2010 tại Trường Đại học Y dược Cần Thơ Tốt nghiệp Thạc sĩ Y học chuyên ngành Nội khoa năm 2018 tại Trường Đại học Y dược Thành phố
BS.CKI DANH THỊ THOA,Nhân viên,nam,20210705,,,,0,4713,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2013 tại Trường Đại học Y Dược TP Hồ Chí Minh Tốt nghiệp Chuyên khoa cấp 1 chuyên ngành Nội khoa năm 2018 tại Trường Đại học Y Dược
ThS.BS TRẦN PHI LONG,Nhân viên,nam,20210705,,,,0,4564,,Giới tính: Nam Quá trình đào tạo: 2006 - 2012: Bác Sĩ Đa Khoa - Đại Học Y Khoa Phạm Ngọc Thạch 2014 - 2017: Tốt Nghiệp Thạc Sỹ - Đại Học Y Dược Tp HCM. 2018 : Chứng chỉ siêu âm tim - Bệnh viện
BS.CKI NGUYỄN QUANG HƯNG,Nhân viên,nam,20210705,,,,0,4862,,"Giới tính: Nam Quá trình đào tạo: 2006 - 2012: Đại Học Y Dược Cần Thơ 2016 - 2018: Đào tạo sau Đại học CK1 Ngoại Thần Kinh Cột sống, Đại Học Y Dược Tp HCM. Kinh nghiệm làm việc: 2012 - 201"
BS. HUỲNH QUỐC TOÀN,Nhân viên,nam,20210705,,,,0,4573,,Giới tính: Nam Quá trình đào tạo: 2012 - 2018: Đại Học Y Khoa  - Trường Đại Học Võ Trường Toản Kinh nghiệm làm việc: 2018 - 2020: Khoa Cấp Cứu - Bệnh viện Đa Khoa Trung uơng Cần Thơ Chức vụ (
BS. NGUYỄN THANH LIỆT,Nhân viên,nam,20210705,,,,0,,,Giới tính: Nam Quá trình đào tạo: Kinh nghiệm làm việc: Chức vụ (Công việc) tại Bệnh Viện Đa Khoa Quốc Tế S.I.S Cần Thơ: Bác sĩ điều trị
BS. CKI NGUYỄN HỮU VỊNH,Phó khoa,nam,20210705,,,,0,4572,,Giới tính: Nam Quá trình đào tạo: 2008 - 2014: Đại Học Y Dược Cần Thơ 2018 - 2020: Tốt nghiệp Bác sĩ CKI - Đại Học Y Dược Cần Thơ Kinh nghiệm làm việc: 2014 - 2017: Khoa Cấp Cứu - Bệnh viện
BS.CKII NGÔ MINH TUẤN,Trưởng khoa,nam,20221101,,,,0,5330,,Giới tính: Nam Quá trình đào tạo: Chứng chỉ DSA can thiệp mạch tổng quát tại Bệnh viện Chợ Rẫy năm 2008. Chứng chỉ Chụp và can thiệp mạch máu Thần Kinh tại Đại học Y khoa Tp. Hồ Chí Minh năm 200
BS.CKI LƯU THỊ PHƯƠNG,Trưởng khoa,nam,20221101,,,,0,9214,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2004 tại Trường Đại học Y dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa Cấp 1 chuyên nghành Nội tổng quát năm 2009 tại Trường Đại học
TS. BS. TRẦN THỊ CẨM NHUNG,Trưởng khoa,nam,19840529,0902951984,,,0,9458,,"Giới tính: Nữ Quá trình đạo tạo: Tốt nghiệp Bs Đa Khoa, tại Trường Đại Học Y Dược Cần Thơ, năm 2008 Tốt nghiệp Thạc sĩ chuyên ngành Gây mê hồi sức, tại Đại Học Y Dược TP Hồ Chí Minh, năm 2"
BS. CKII PHƯƠNG HỒNG THỌ,Phó khoa,nam,19900101,,,,3,9547,,Giới tính: Nam Quá trình đạo tạo: Tốt nghiệp Bác sĩ Đa khoa năm 1999 tại trường Đại Học Cần Thơ Tốt nghiệp Bác sĩ CKI chuyên ngành Thần kinh năm 2015 tại Trường Đại Học Y Dược TP.HCM Tốt nghiệ
THS.BS.CKI NGUYỄN KIM PHỤNG,Phó khoa,nam,19900101,,,,1,9542,,Giới tính: Nữ Quá trình đạo tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2015 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ nội trú năm 2018 tại trường Đại học Y Dược Cần Thơ Chứng chỉ thực hành h
CNĐD. NGUYỄN THỊ THÚY QUYÊN,Điều dưỡng trưởng,nam,19900101,,,,4,9546,,Giới tính: Nữ Quá trình đào tạo: Bổ túc kiến thức về Điều dưỡng Hồi sức cấp cứu Bệnh viện Chợ Rẫy. Quản lý bệnh viện (Đại học Y dược Cần Thơ) Quản lý điều dưỡng (Đại học Y dược Cần Thơ) Phươn
CN. NGUYỄN THANH HỒNG,Điều dưỡng trưởng,nữ,19800101,,,,4,9572,,Giới tính: Nữ Quá trình đào tạo: 2014: Tốt nghiệp Cao đẳng Điều dưỡng trường Cao đẳng Y tế Cần Thơ. 2024: Tốt nghiệp Cử nhân Điều dưỡng trường Đại Học Cửu Long. Chứng nhận khóa học “Quy
BS. Lê Xuân Phượng,Trưởng khoa,nữ,19800101,,,,0,12300,,Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa tại trường Đại học Y dược Cần Thơ Chứng nhận chuyên khoa Vật lý trị liệu của Bệnh viện Chợ Rẫy Chứng chỉ Âm ngữ trị liệu của Bệnh viện Chợ Rẫy
BS. Nguyễn Hữu Thơ,Nhân viên,nam,19800101,,,,0,,,Chứng chỉ định hướng Phục hồi chức năng của Trường Đại học Y dược Cần Thơ Chứng chỉ Âm ngữ trị liệu của Trường Đại học Y dược Cần Thơ
BS.CKI Trương Đan Kha,Phó khoa,nam,19800101,,,,3,12299,,Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Phục hồi chức năng tại trường Đại học Y dược Cần Thơ. Chứng chỉ định hướng Phục hồi chức năng của Bệnh viện Phục hồi chức năng và điều trị bệnh nghề nghi
Trần Công Minh,Trưởng khoa,nam,19800101,0908999793,,,0,,,Giới tính: Nam Quá trình đạo tạo: Chuyên khoa I xét nghiệm Hơn 10 năm kinh nghiệm làm việc trong Phòng xét nghiệm
Trần Thị Diễm Trang,Điều dưỡng trưởng,nam,19800101,0383784374,,,0,,,Giới tính: Nam Quá trình đạo tạo: Cử nhân xét nghiệm Hơn 10 năm kinh nghiệm làm việc tại Phòng xét nghiệm
THS.QLBV NGUYỄN VĂN TRUNG,Trưởng phòng,nam,19800101,,,,0,9666,,Giới tính: Nam Quá trình đạo tạo: Tốt nghiệp Thạc sĩ Quản lý bệnh viện tại Trường Đại học Y tế Công cộng Hà Nội năm 2017. Chứng chỉ Quản lý nhà nước tại Trường chính trị tỉnh Đồng Tháp năm 2010.
BS.CK1 Nguyễn Nhựt Thái,Phó khoa,nam,19800101,,,,1,12894,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2003 tại Trường Đại học Y dược Thành Phố Hồ Chí Minh. Tốt nghiệp Chuyên Khoa 1 chuyên ngành Ngoại Thần Kinh năm 2008 tại Trườn
THS.BS Nguyễn Anh Trung,Phó khoa,nam,19800101,,,,3,10261,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y dược Cần Thơ . Chứng chỉ đào tạo phẫu thuật thay khớp tại Trường Đại học Y dược Thành phố Hồ Chí Minh Chứng chỉ đào tạo phẫu thuật
BS.CK1Nguyễn Quang Hưng,Nhân viên,nam,19800101,,,,4,4862,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2012 tại Trường Đại học Y dược Cần Thơ. Tốt nghiệp Chuyên Khoa 1 chuyên ngành Ngoại Thần Kinh năm 2018 tại Trường Đại học Y dư
KTV. Hồ Ngọc Diễm,Điều dưỡng trưởng,nữ,19900101,,,,4,12301,,Quá trình đào tạo: Chứng chỉ đào tạo liên tục: Quản lý điều dưỡng năm 2021 Chứng nhận Các tác nhân vật lý trong thực hành lâm sàng nâng cao của Bệnh viện Chợ Rẫy. Chứng chỉ ngoại ngữ tiếng Anh
Ths MAI TRUNG KIÊN,Phó phòng,nam,19800101,,,,0,10284,,Giới tính: Nam Quá trình đạo tạo: Tốt nghiệp Kỹ sư Tin học năm 2011 Tốt nghiệp Thạc sĩ Quản lý Kinh tế năm 2022 Kinh nghiệm làm việc: 2011 2014: Phòng IT Công ty Cổ phần Mía đường
ĐD. Trần Việt Khanh,Điều dưỡng trưởng,nam,19800101,,,,0,,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Cử nhân Điều dưỡng đa khoa năm 2011 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Cử nhân Ngôn ngữ Anh năm 2016 tại Trường Đại Học Cần Thơ Chứ
THS.BS NGUYỄN NGỌC ANH THƯ,Phó khoa,nữ,19800101,,,,0,11310,,Giới tính: Nữ Qúa trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa Trường Đại Học Y Dược Cần Thơ- năm 2011 Đào tạo liên tục chuyên ngành Nội tim mạch- Bệnh viện Chợ Rẫy năm 2012 Tốt nghiệp Cao học nộ
TS.BS Trần Chí Cường,Trưởng khoa,nam,19770101,,,,0,12899,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y khoa năm 2000 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa 1 chuyên ngành Phẫu thuật Thần kinh (Ngoại Thần kinh) năm 200
BÁC SĨ NỘI TRÚ- CHUYÊN KHOA 1 TRƯƠNG LÊ ANH KIỆT,Nhân viên,nam,19800101,,,,3,13612,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Y khoa loại giỏi Trường Đại học Y dược Cần Thơ. Tốt nghiệp Bác sĩ nội trú- Chuyên khoa 1 Tai Mũi Họng bằng Xuất sắc Trường Đại học Y dược
BS.CKI. Võ Văn Năm,Nhân viên,nam,19800101,,,,4,13613,,Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Y khoa tại Trường Đại học Y Dược Cần Thơ năm 2020 Tốt nghiệp Bác sĩ nội trú- Chuyên Khoa 1 Chuyên ngành Tai Mũi Họng tại Trường Đại học Y
BS.CKI. Huỳnh Thị Nhật Bình,Nhân viên,nữ,19800101,,,,5,13616,,Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ Y khoa tại Trường Đại học Y Dược Cần Thơ năm 2021 Tốt nghiệp Bác sĩ nội trú- Chuyên Khoa 1 Chuyên ngành Tai Mũi Họng tại Trường Đại học Y
1 Name Position Gender Birthday Phone Email Address Order ImageID ImageURL Content
2 Th.S HOÀNG KIM NGA Phó giám đốc tài chính nữ 20181003 123 bbb@gmail.com fdfdfdfd 2 5394 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Thạc sĩ Tài chính - Ngân hàng năm 2010 tại CFVG TP.HCM  (Liên kết giữa ĐH Kinh tế và Đại Học EFCG Pháp) Tốt nghiệp Cử nhân Anh văn năm 1997 tại Đại họ
3 TS.BS Tăng Hà Nam Anh Cố vấn chuyên môn nam 20181002 012455221 bbb@gmail.com adfsda 1 1713 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Tiến Sĩ Y Khoa chuyên ngành Chấn Thương Chỉnh Hình tại Trường Đại Học Y Dược TP. Hồ Chí Minh Kinh nghiệm làm việc: Trưởng Khoa Chấn Thương Chỉnh Hì
4 TS.BS TRẦN CHÍ CƯỜNG Giám đốc nam 20181025 0 12940 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y khoa năm 2000 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa 1 chuyên ngành Phẫu thuật Thần kinh (Ngoại Thần kinh) năm 200
5 BS.CKII PHAN TRỊNH MINH HIẾU Phó giám đốc điều hành nữ 20181011 0123456789 abc@gmail.com abc 1 3542 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Đại học Y Khoa năm 2000 tại Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Sản Phụ Khoa năm 2004 tại Đại học Y Dược TP. Hồ Chí Mi
6 DS. Lê Hoàng Mai Trưởng khoa nữ 19751016 1 4589 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Dược Sĩ Đại Học năm 1972 tại Trường Đại Học Dược Khoa Sài Gòn Chứng chỉ đào tạo chuyên môn Dinh Dưỡng năm 1990 của Đại Học Mahidol - Thái Lan Chứng
7 ĐD. Nguyễn Thị Thiên Trang Điều dưỡng trưởng nữ 19800207 4 2249 Giới tính: Nữ Quá trình đào tạo: Chứng chỉ đào tạo Điều dưỡng năm 1998 của TTĐT & BDCBYT - TP. Hồ Chí Minh Chứng chỉ đào tạo Năng lực quản lý cấp trung năm 2017 của FBT School of Business
8 BS.CKII HUỲNH QUỐC SĨ Trưởng khoa nam 20180405 0 4570 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2009 tại Trường Đại học Y Dược Cần Thơ Tốt nghiệp Thạc sĩ chuyên ngành Nội Tổng quát năm 2015 tại Trường Đại học Y Dược Cần Thơ
9 BS.CKI Nguyễn Thị Hạnh Phó khoa nữ 19790413 1 4552 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2005 tại Trường Đại học Y Dược Hải Phòng Tốt nghiệp Bác sĩ Chuyên khoa I năm 2014 tại Trường Đại học Y Dược TP.HCM Kinh nghiệm làm
10 ĐD. Nguyễn Thị Thủy Tiên Điều dưỡng trưởng nữ 20181101 0123456789 abc@gmail.com fdfdfdfd 1 8010 Giới tính: Nữ Quá trình đào tạo: Chứng chỉ đào tạo Điều dưỡng đa khoa năm 2016 của Trường Cao Đẳng Y Tế Tiền Giang Cử nhân: Điều dưỡng – Trường Đại Học Cửu Long (năm 2021) Chứng chỉ: Quản lí đ
11 Đinh Hoàng Khoa Trưởng phòng nam 20181205 0 Trưởng phòng
12 Nguyễn Văn Chiến Nhân viên nam 19991214 1 Nhân viên
13 Phan Minh Quang Nhân viên nam 20011211 2 Nhân viên
14 BS.CKI PHƯƠNG HỒNG THỌ Phó khoa nam 20181220 1 4588 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 1999 tại Trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Nội Thần kinh năm 2015 tại Trường Đại học Y Dư
15 ThS.BS NGUYỄN ANH TRUNG Phó khoa nam 20181220 0 4587 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Thạc sĩ chuyên ngành Chấn thương Chỉnh hình tại Trường Đại học Y Dược TP.HCM Chứng chỉ đào tạo Thay khớp của Trường Đại học Y Dược TP.HCM Chứng chỉ
16 BS.CKI NGÔ THỊ NHƯ THÙY Nhân viên nữ 20181220 2 1133 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác Sĩ Đa Khoa năm 2000 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Nhi Khoa năm 2009 tại Trường Đại Học Y Dược TP.
17 ĐD. Lê Quang Khải Điều dưỡng trưởng nam 20190301 1 4586 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Trung cấp Điều dưỡng Đa khoa năm 2010 tại Trường Trung cấp Y tế Vĩnh Long Chứng chỉ đào tạo liên tục - Chứng chỉ CRRT năm 2014 của Bệnh viện 115 TP.
18 ĐD. Đặng Thị Ngọc Trân Điều dưỡng trưởng nữ 20190301 1 1854 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Trung cấp Điều dưỡng đa khoa năm 2006 tại Trường trung học y tế Bạc Liêu Tốt nghiệp Cao đẳng Điều dưỡng đa khoa năm 2018 tại Trường Cao đẳng Y tế Cần
19 ĐD. Trương Thị Ngọc Linh Điều dưỡng trưởng nữ 20190301 1 4585 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Trung cấp Điều dưỡng đa khoa năm 2009 tại Trường Cao đẳng Y tế Cần Thơ Chứng chỉ Điều dưỡng năm 2012 tại Bệnh Viện Đa Khoa Hoàn Mỹ Cửu Long Kinh nghi
20 ĐD. Trần Việt Khanh Điều dưỡng trưởng nam 20190301 1 4583 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Cử nhân Điều dưỡng đa khoa năm 2011 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Cử nhân Ngôn ngữ Anh năm 2016 tại Trường Đại Học Cần Thơ Chứng chỉ
21 BS.CKII LÊ UYỂN DIỄM Trưởng khoa nữ 20190301 0 1863 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 1996 tại Trường Đại Học Y Dược Huế Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Chẩn đoán hình ảnh năm 2005 tại Trường Đại Học Y Dư
22 TS.BS NGUYỄN HUY THẮNG Cố vấn chuyên môn nam 20190301 0955555555 hh@gmail.com Can tho 1 1865 Giới tính: Nam Bằng cấp chuyên môn: Tốt nghiệp trường Đại học Y dược TP Hồ Chí Minh năm 1993 Bằng Tiến sĩ - Trường Đại học Y dược TP Hồ Chí Minh năm 2012 (Tiến sĩ chuyên ngành Thần kinh) Đào
23 BS.CKI TRẦN ÁI THANH Phó khoa nữ 20190521 1 4563 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác Sĩ Đa Khoa năm 2008 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Nội - Tổng quát năm 2016 tại Trường Đại Học Y D
24 BS.CKI TÔ VĂN TÂN Phó khoa nam 20190522 1 4582 Giới tính: Nam Quá trình đào tạo:  Tốt nghiệp Bác Sĩ Đa Khoa năm 2011 tại Trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa Cấp I Nội Tổng quát năm 2018 tại Trường Đại học Y Dược Cần T
25 BS.CKII BÙI THỊ BÍCH Phó khoa nữ 20190924 1 4571 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Chẩn đoán hình ảnh năm 2011 tại Trường Đại Học Y Dược TP. Hồ Chí Minh Tốt nghiệp Bác Sĩ Chuyên Khoa II chuyên ngành
26 ThS.BS NGUYỄN MINH NGUYỆT Nhân viên nam 20200714 0 4581 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2007 tại Trường Đại Học Y Dược TP. Cần Thơ Tốt nghiệp Thạc sĩ Nội tổng quát năm 2014 tại Trường Đại Học Y Dược TP. Hồ Chí Minh Ch
27 BS.CKII CHÂU THỊ THÚY LIỄU Trưởng khoa nữ 19800101 2 aa@gmail.com 1 5453 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 1998 tại Trường Đại học Cần Thơ Tốt nghiệp Bác Sĩ Chuyên Khoa I chuyên ngành Nội thần kinh năm 2006 tại Trường Đại Học Y Dược TP. H
28 BS NGUYỄN ĐÀO NHẬT HUY Nhân viên nam 20200724 0 4561 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2012 tại trường Đại học Cần Thơ Chứng nhận Kiến tập Tim mạch Can thiệp năm 2013 tại Bệnh viện Chợ Rẫy Chứng nhận Siêu âm Tim năm 2019
29 ThS.BS TRẦN MINH LUẬN Nhân viên nam 20200724 0 3153 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2010 tại trường Đại học Cần Thơ Tốt nghiệp Thạc sĩ Thần kinh - Tâm thần năm 2016 tại trường Đại học Y Dược TP.HCM Chứng nhận Chup - C
30 BS. TÔ THANH ỬNG Nhân viên nam 20200724 0 4576 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2012 tại trường Đại học Y Dược Cần Thơ Chứng nhận Siêu âm tổng quát năm 2014 tại trường Đại học Y Dược Cần Thơ Chứng nhận Nội soi Phế
31 ThS.BS NGUYỄN LƯU GIANG Trưởng khoa nam 20200724 0 4580 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2011 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Thạc sĩ Y học năm 2016 tại trường Đại học Y Dược Cần Thơ Chứng chỉ Định hướng chẩn
32 ThS.BS NGUYỄN MẠNH CƯỜNG Trưởng khoa nam 20200724 0 4562 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2011 trường Đại học Y dược Cần Thơ Tốt nghiệp Thạc Sĩ Y học chuyên ngành Nội khoa năm 2016 tại trường Đại học Y dược Cần Thơ Giấ
33 ThS.BS TRƯƠNG PHẠM VĨNH LỄ Trưởng phòng nam 19800101 3 4579 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Y đa khoa năm 2011 tại trường Đại học Cần Thơ Tốt nghiệp Thạc sĩ Thần kinh năm 2016 tại trường Đại học Y Dược Tp.HCM Chứng nhận Siêu âm tổng quát n
34 ThS.BS TRẦM THỊ KIM SA Nhân viên nam 20210705 0 4578 Giới tính: Nữ Quá trình đào tạo: ● Tốt nghiệp Y đa khoa năm 2000 tại trường Đại học Cần Thơ ● Tốt nghiệp Thạc sĩ Nội khoa năm 2007 tại trường Đại học Y Dược Cần Thơ ● Chứng nhận Đào tạo Hồi sức cấp
35 BS. TRẦN TIẾN THÀNH Nhân viên nam 20210705 0 4623 Giới tính: Nam Quá trình đào tạo: 2010 - 2016: Bác sĩ Đa Khoa  Đại Học Y Dược Cần Thơ 2019 - 2021: Đào tạo BS.CKI Nội Thần Kinh  - Đại Học Y Dược Tp HCM. Chứng nhận cập nhật chẩn đoán điều trị
36 BS. DƯƠNG HOÀNG LINH Nhân viên nam 20210705 0 4575 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y Dược Cần Thơ năm 2017 Kinh nghiệm làm việc: 7/2017-9/2020: BS Khoa hồi sức cấp cứu - chống độc Trung tâm y tế huyện Gò Quao, tỉnh Kiên Gi
37 BS.CKI PHAN THỊ THU Nhân viên nam 20210706 0 Giới tính: Nữ Quá trình đào tạo: 1997 : Tốt nghiệp Đại Học Y Dược TP Hồ Chí Minh 2008 : Tốt nghiệp CKI Nội Thần Kinh Đại Học Y Dược TP. Hồ Chí Minh Kinh nghiệm làm việc: Bác sĩ Khoa Nội tổng
38 ThS.BS NGUYỄN HẢI NGUYÊN Nhân viên nam 20210705 0 4565 Giới tính: Nam Quá trình đào tạo: 2012 Tốt nghiệp Bác sĩ Đa Khoa Đại Học Y Dược Cần Thơ Kinh nghiệm làm việc: 2012-2015 Bác sĩ nội khoa Bệnh viện Đa Học Y Dược Cần Thơ 2015-2018 Bác sĩ nội k
39 ThS.BS LƯU TRÚC PHƯƠNG Phó khoa nam 20210706 0358032332 drphuongluu@dotquy.vn 1 9459 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2010 tại Trường Đại học Y dược Cần Thơ Tốt nghiệp Thạc sĩ Y học chuyên ngành Nội khoa năm 2018 tại Trường Đại học Y dược Thành phố
40 BS.CKI DANH THỊ THOA Nhân viên nam 20210705 0 4713 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2013 tại Trường Đại học Y Dược TP Hồ Chí Minh Tốt nghiệp Chuyên khoa cấp 1 chuyên ngành Nội khoa năm 2018 tại Trường Đại học Y Dược
41 ThS.BS TRẦN PHI LONG Nhân viên nam 20210705 0 4564 Giới tính: Nam Quá trình đào tạo: 2006 - 2012: Bác Sĩ Đa Khoa - Đại Học Y Khoa Phạm Ngọc Thạch 2014 - 2017: Tốt Nghiệp Thạc Sỹ - Đại Học Y Dược Tp HCM. 2018 : Chứng chỉ siêu âm tim - Bệnh viện
42 BS.CKI NGUYỄN QUANG HƯNG Nhân viên nam 20210705 0 4862 Giới tính: Nam Quá trình đào tạo: 2006 - 2012: Đại Học Y Dược Cần Thơ 2016 - 2018: Đào tạo sau Đại học CK1 Ngoại Thần Kinh – Cột sống, Đại Học Y Dược Tp HCM. Kinh nghiệm làm việc: 2012 - 201
43 BS. HUỲNH QUỐC TOÀN Nhân viên nam 20210705 0 4573 Giới tính: Nam Quá trình đào tạo: 2012 - 2018: Đại Học Y Khoa  - Trường Đại Học Võ Trường Toản Kinh nghiệm làm việc: 2018 - 2020: Khoa Cấp Cứu - Bệnh viện Đa Khoa Trung uơng Cần Thơ Chức vụ (
44 BS. NGUYỄN THANH LIỆT Nhân viên nam 20210705 0 Giới tính: Nam Quá trình đào tạo: Kinh nghiệm làm việc: Chức vụ (Công việc) tại Bệnh Viện Đa Khoa Quốc Tế S.I.S Cần Thơ: Bác sĩ điều trị
45 BS. CKI NGUYỄN HỮU VỊNH Phó khoa nam 20210705 0 4572 Giới tính: Nam Quá trình đào tạo: 2008 - 2014: Đại Học Y Dược Cần Thơ 2018 - 2020: Tốt nghiệp Bác sĩ CKI - Đại Học Y Dược Cần Thơ Kinh nghiệm làm việc: 2014 - 2017: Khoa Cấp Cứu - Bệnh viện
46 BS.CKII NGÔ MINH TUẤN Trưởng khoa nam 20221101 0 5330 Giới tính: Nam Quá trình đào tạo: Chứng chỉ DSA can thiệp mạch tổng quát tại Bệnh viện Chợ Rẫy năm 2008. Chứng chỉ Chụp và can thiệp mạch máu Thần Kinh tại Đại học Y khoa Tp. Hồ Chí Minh năm 200
47 BS.CKI LƯU THỊ PHƯƠNG Trưởng khoa nam 20221101 0 9214 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2004 tại Trường Đại học Y dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa Cấp 1 chuyên nghành Nội tổng quát năm 2009 tại Trường Đại học
48 TS. BS. TRẦN THỊ CẨM NHUNG Trưởng khoa nam 19840529 0902951984 0 9458 Giới tính: Nữ Quá trình đạo tạo: Tốt nghiệp Bs Đa Khoa, tại Trường Đại Học Y Dược Cần Thơ, năm 2008 Tốt nghiệp Thạc sĩ chuyên ngành Gây mê hồi sức, tại Đại Học Y Dược TP Hồ Chí Minh, năm 2
49 BS. CKII PHƯƠNG HỒNG THỌ Phó khoa nam 19900101 3 9547 Giới tính: Nam Quá trình đạo tạo: Tốt nghiệp Bác sĩ Đa khoa năm 1999 tại trường Đại Học Cần Thơ Tốt nghiệp Bác sĩ CKI chuyên ngành Thần kinh năm 2015 tại Trường Đại Học Y Dược TP.HCM Tốt nghiệ
50 THS.BS.CKI NGUYỄN KIM PHỤNG Phó khoa nam 19900101 1 9542 Giới tính: Nữ Quá trình đạo tạo: Tốt nghiệp Bác sĩ Đa khoa năm 2015 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ nội trú năm 2018 tại trường Đại học Y Dược Cần Thơ Chứng chỉ thực hành h
51 CNĐD. NGUYỄN THỊ THÚY QUYÊN Điều dưỡng trưởng nam 19900101 4 9546 Giới tính: Nữ Quá trình đào tạo: Bổ túc kiến thức về Điều dưỡng Hồi sức cấp cứu Bệnh viện Chợ Rẫy. Quản lý bệnh viện (Đại học Y dược Cần Thơ) Quản lý điều dưỡng (Đại học Y dược Cần Thơ) Phươn
52 CN. NGUYỄN THANH HỒNG Điều dưỡng trưởng nữ 19800101 4 9572 Giới tính: Nữ Quá trình đào tạo: 2014: Tốt nghiệp Cao đẳng Điều dưỡng trường Cao đẳng Y tế Cần Thơ. 2024: Tốt nghiệp Cử nhân Điều dưỡng trường Đại Học Cửu Long. Chứng nhận khóa học “Quy
53 BS. Lê Xuân Phượng Trưởng khoa nữ 19800101 0 12300 Quá trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa tại trường Đại học Y dược Cần Thơ Chứng nhận chuyên khoa Vật lý trị liệu của Bệnh viện Chợ Rẫy Chứng chỉ Âm ngữ trị liệu của Bệnh viện Chợ Rẫy
54 BS. Nguyễn Hữu Thơ Nhân viên nam 19800101 0 Chứng chỉ định hướng Phục hồi chức năng của Trường Đại học Y dược Cần Thơ Chứng chỉ Âm ngữ trị liệu của Trường Đại học Y dược Cần Thơ
55 BS.CKI Trương Đan Kha Phó khoa nam 19800101 3 12299 Tốt nghiệp Bác sĩ Chuyên khoa I chuyên ngành Phục hồi chức năng tại trường Đại học Y dược Cần Thơ. Chứng chỉ định hướng Phục hồi chức năng của Bệnh viện Phục hồi chức năng và điều trị bệnh nghề nghi
56 Trần Công Minh Trưởng khoa nam 19800101 0908999793 0 Giới tính: Nam Quá trình đạo tạo: Chuyên khoa I xét nghiệm Hơn 10 năm kinh nghiệm làm việc trong Phòng xét nghiệm
57 Trần Thị Diễm Trang Điều dưỡng trưởng nam 19800101 0383784374 0 Giới tính: Nam Quá trình đạo tạo: Cử nhân xét nghiệm Hơn 10 năm kinh nghiệm làm việc tại Phòng xét nghiệm
58 THS.QLBV NGUYỄN VĂN TRUNG Trưởng phòng nam 19800101 0 9666 Giới tính: Nam Quá trình đạo tạo: Tốt nghiệp Thạc sĩ Quản lý bệnh viện tại Trường Đại học Y tế Công cộng Hà Nội năm 2017. Chứng chỉ Quản lý nhà nước tại Trường chính trị tỉnh Đồng Tháp năm 2010.
59 BS.CK1 Nguyễn Nhựt Thái Phó khoa nam 19800101 1 12894 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2003 tại Trường Đại học Y dược Thành Phố Hồ Chí Minh. Tốt nghiệp Chuyên Khoa 1 chuyên ngành Ngoại Thần Kinh năm 2008 tại Trườn
60 THS.BS Nguyễn Anh Trung Phó khoa nam 19800101 3 10261 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y dược Cần Thơ . Chứng chỉ đào tạo phẫu thuật thay khớp tại Trường Đại học Y dược Thành phố Hồ Chí Minh Chứng chỉ đào tạo phẫu thuật
61 BS.CK1Nguyễn Quang Hưng Nhân viên nam 19800101 4 4862 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ đa khoa năm 2012 tại Trường Đại học Y dược Cần Thơ. Tốt nghiệp Chuyên Khoa 1 chuyên ngành Ngoại Thần Kinh năm 2018 tại Trường Đại học Y dư
62 KTV. Hồ Ngọc Diễm Điều dưỡng trưởng nữ 19900101 4 12301 Quá trình đào tạo: Chứng chỉ đào tạo liên tục: Quản lý điều dưỡng năm 2021 Chứng nhận Các tác nhân vật lý trong thực hành lâm sàng nâng cao của Bệnh viện Chợ Rẫy. Chứng chỉ ngoại ngữ tiếng Anh
63 Ths MAI TRUNG KIÊN Phó phòng nam 19800101 0 10284 Giới tính: Nam Quá trình đạo tạo: Tốt nghiệp Kỹ sư Tin học năm 2011 Tốt nghiệp Thạc sĩ Quản lý Kinh tế năm 2022 Kinh nghiệm làm việc: 2011 – 2014: Phòng IT – Công ty Cổ phần Mía đường
64 ĐD. Trần Việt Khanh Điều dưỡng trưởng nam 19800101 0 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Cử nhân Điều dưỡng đa khoa năm 2011 tại Trường Đại Học Y Dược Cần Thơ Tốt nghiệp Cử nhân Ngôn ngữ Anh năm 2016 tại Trường Đại Học Cần Thơ Chứ
65 THS.BS NGUYỄN NGỌC ANH THƯ Phó khoa nữ 19800101 0 11310 Giới tính: Nữ Qúa trình đào tạo: Tốt nghiệp Bác sĩ Đa khoa Trường Đại Học Y Dược Cần Thơ- năm 2011 Đào tạo liên tục chuyên ngành Nội tim mạch- Bệnh viện Chợ Rẫy năm 2012 Tốt nghiệp Cao học nộ
66 TS.BS Trần Chí Cường Trưởng khoa nam 19770101 0 12899 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Đại học Y khoa năm 2000 tại trường Đại học Y Dược Cần Thơ Tốt nghiệp Bác sĩ Chuyên khoa 1 chuyên ngành Phẫu thuật Thần kinh (Ngoại Thần kinh) năm 200
67 BÁC SĨ NỘI TRÚ- CHUYÊN KHOA 1 TRƯƠNG LÊ ANH KIỆT Nhân viên nam 19800101 3 13612 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Y khoa loại giỏi Trường Đại học Y dược Cần Thơ. Tốt nghiệp Bác sĩ nội trú- Chuyên khoa 1 Tai Mũi Họng bằng Xuất sắc Trường Đại học Y dược
68 BS.CKI. Võ Văn Năm Nhân viên nam 19800101 4 13613 Giới tính: Nam Quá trình đào tạo: Tốt nghiệp Bác sĩ Y khoa tại Trường Đại học Y Dược Cần Thơ năm 2020 Tốt nghiệp Bác sĩ nội trú- Chuyên Khoa 1 Chuyên ngành Tai Mũi Họng tại Trường Đại học Y
69 BS.CKI. Huỳnh Thị Nhật Bình Nhân viên nữ 19800101 5 13616 Giới tính: Nữ Quá trình đào tạo: Tốt nghiệp Bác sĩ Y khoa tại Trường Đại học Y Dược Cần Thơ năm 2021 Tốt nghiệp Bác sĩ nội trú- Chuyên Khoa 1 Chuyên ngành Tai Mũi Họng tại Trường Đại học Y
+17
View File
@@ -0,0 +1,17 @@
Title: Th.S HOÀNG KIM NGA
image = 5394
gender = nữ
position = Phó giám đốc tài chính
birthday = 20181003
phone_number = 123
email = bbb@gmail.com
address = fdfdfdfd
order = 2
tg_post_views_count = 897
type = 0
sequence = 0
<strong>Giới tính: </strong>Nữ
<strong>Quá trình đào tạo:</strong>
<ul>
<li>Tốt nghiệp Thạc sĩ Tà
@@ -0,0 +1,55 @@
import csv
import sys
csv_file = '/home/x79/sisvietnamvn_01/Media/HÌNH ẢNH BÁC SĨ NGỒI PHÒNG KHÁM/ALL_IMAGES/DANH SÁCH BÁC SĨ NGỒI PHÒNG KHÁM.csv'
sql_statements = []
try:
with open(csv_file, 'r', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
specialties = set()
doctor_mappings = []
for row in reader:
doctor_code = row.get('MSNV', '').strip()
specialty_name = row.get('Khoa / phòng', '').strip()
if doctor_code and specialty_name:
specialties.add(specialty_name)
doctor_mappings.append((doctor_code, specialty_name))
# We will assume there's a sequence sis_specialty_seq or we just use IDs starting from 1000
# Wait, the best way in Oracle is to use MERGE or just PL/SQL block
plsql = "BEGIN\n"
for spec in specialties:
safe_spec = spec.replace("'", "''")
plsql += f"""
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM sis_specialty WHERE name = '{safe_spec}';
IF v_count = 0 THEN
INSERT INTO sis_specialty (id, name, active, created_by, created_date, last_modified_by, last_modified_date)
VALUES (sequenceGenerator.NEXTVAL, '{safe_spec}', 1, 'system', sysdate, 'system', sysdate);
END IF;
END;
"""
for doc_code, spec in doctor_mappings:
safe_spec = spec.replace("'", "''")
safe_code = doc_code.replace("'", "''")
plsql += f"""
UPDATE sis_doctor
SET specialty_id = (SELECT id FROM sis_specialty WHERE name = '{safe_spec}' FETCH FIRST 1 ROWS ONLY)
WHERE doctor_code = '{safe_code}';
"""
plsql += "COMMIT;\nEND;\n/"
with open('/home/x79/sisvietnamvn_01/sisvietnamvn_main/src/main/resources/config/liquibase/data/map_specialties.sql', 'w', encoding='utf-8') as out_f:
out_f.write(plsql)
print("Generated map_specialties.sql")
except Exception as e:
print(f"Error: {e}")
@@ -0,0 +1,178 @@
import csv
import xml.etree.ElementTree as ET
import sys
import os
csv_file = '/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/DS_BacSi/DANH SÁCH BÁC SĨ NGỒI PHÒNG KHÁM_THÔNG TIN CHI TIẾT_FULL.csv'
xml_file = '/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/DS_BacSi/bnhvinakhoaquctsiscnth.WordPress.2026-07-28 (5).xml'
# Read CSV
csv_doctors = {}
with open(csv_file, 'r', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
for row in reader:
name = row.get('Họ và tên', '').strip()
if not name:
continue
msnv = row.get('MSNV', '').strip()
title = row.get('Chức danh', '').strip()
degree = row.get('Trình độ chuyên môn', '').strip()
specialty = row.get('Khoa/ Phòng', '').strip()
education = row.get('Quá trình đào tạo', '').strip()
experience = row.get('Quá trình công tác', '').strip()
achievements = row.get('Hiệp hội chuyên môn', '').strip() + '\n' + row.get('Ghi chú', '').strip()
csv_doctors[name.lower()] = {
'name': name,
'doctor_code': msnv,
'title': degree,
'position': title,
'specialty': specialty,
'education': education,
'experience': experience,
'achievements': achievements,
'active': 1 if msnv else 0
}
# Read XML
xml_doctors = {}
tree = ET.parse(xml_file)
root = tree.getroot()
ns = {
'wp': 'http://wordpress.org/export/1.2/',
'content': 'http://purl.org/rss/1.0/modules/content/'
}
for channel in root.findall('channel'):
for item in channel.findall('item'):
post_type = item.find('wp:post_type', ns)
if post_type is None or post_type.text != 'doctor':
continue
title_elem = item.find('title')
if title_elem is None or not title_elem.text:
continue
name = title_elem.text.strip()
if not name:
continue
doc = {'name': name}
# Extract department category
for cat in item.findall('category'):
if cat.get('domain') == 'department':
doc['specialty'] = cat.text.strip()
break
for meta in item.findall('wp:postmeta', ns):
key = meta.find('wp:meta_key', ns)
val = meta.find('wp:meta_value', ns)
if key is not None and val is not None and val.text:
k = key.text
v = val.text.strip()
if k == 'doctor_information_position':
doc['position'] = v
elif k == 'doctor_information_specialty':
doc['specialty'] = v
elif k == 'doctor_information_experience':
doc['experience'] = v
elif k == 'doctor_information_education':
doc['education'] = v
elif k == 'doctor_information_achievements':
doc['achievements'] = v
elif k == 'doctor_information_gender':
doc['gender'] = v
elif k == 'doctor_information_phone':
doc['phone_number'] = v
elif k == 'doctor_information_email':
doc['email'] = v
elif k == 'doctor_information_avatar':
doc['avatar_url'] = v
elif k == 'doctor_information_booking':
doc['booking_url'] = v
elif k == 'doctor_information_title':
doc['title'] = v
xml_doctors[name.lower()] = doc
# Merge
merged_doctors = {}
for name_key, xml_doc in xml_doctors.items():
merged_doctors[name_key] = xml_doc
# Priorities: CSV overwrites XML
for name_key, csv_doc in csv_doctors.items():
# Find matching XML doctor by checking if CSV name is substring of XML name
matched_xml_key = None
for xml_key in merged_doctors.keys():
# Remove spaces and dashes for comparison
clean_csv = name_key.replace(' ', '').replace('-', '')
clean_xml = xml_key.replace(' ', '').replace('-', '')
if clean_csv in clean_xml or clean_xml in clean_csv:
matched_xml_key = xml_key
break
if matched_xml_key:
existing = merged_doctors[matched_xml_key]
for k, v in csv_doc.items():
if v and str(v).strip():
existing[k] = v
# Ensure we use the clean plain name from CSV, not the long XML title
existing['name'] = csv_doc['name']
else:
merged_doctors[name_key] = csv_doc
# Write SQL
plsql = "BEGIN\n"
plsql += " DELETE FROM sis_doctor_schedule;\n"
plsql += " DELETE FROM sis_doctor;\n"
for doc in merged_doctors.values():
name = doc.get('name', '').replace("'", "''")
code = doc.get('doctor_code', '').replace("'", "''")
title = doc.get('title', '').replace("'", "''")
pos = doc.get('position', '').replace("'", "''")
gender = doc.get('gender', '').replace("'", "''")
phone = doc.get('phone_number', '').replace("'", "''")
email = doc.get('email', '').replace("'", "''")
spec = doc.get('specialty', '').replace("'", "''")
edu = doc.get('education', '').replace("'", "''")
exp = doc.get('experience', '').replace("'", "''")
ach = doc.get('achievements', '').replace("'", "''")
avt = doc.get('avatar_url', '').replace("'", "''")
bk = doc.get('booking_url', '').replace("'", "''")
active = doc.get('active', 0)
plsql += f"""
DECLARE
v_count NUMBER;
v_spec_id NUMBER := NULL;
BEGIN
IF '{spec}' IS NOT NULL THEN
SELECT COUNT(*) INTO v_count FROM sis_specialty WHERE name = '{spec}';
IF v_count = 0 THEN
INSERT INTO sis_specialty (id, name, slug, created_by, created_date, last_modified_by, last_modified_date)
VALUES (sequence_generator.NEXTVAL, '{spec}', '{spec}', 'system', sysdate, 'system', sysdate);
END IF;
SELECT id INTO v_spec_id FROM sis_specialty WHERE name = '{spec}' FETCH FIRST 1 ROWS ONLY;
END IF;
INSERT INTO sis_doctor (
id, name, doctor_code, title, position, gender, phone_number, email,
specialty_id, education, work_experience, achievements, avatar_url, booking_url,
active, display_order, created_by, created_date, last_modified_by, last_modified_date
) VALUES (
sequence_generator.NEXTVAL, '{name}', '{code}', '{title}', '{pos}', '{gender}', '{phone}', '{email}',
v_spec_id, '{edu}', '{exp}', '{ach}', '{avt}', '{bk}',
{active}, 0, 'system', sysdate, 'system', sysdate
);
END;
"""
plsql += "COMMIT;\nEND;\n/"
with open('/home/x79/sisvietnamvn_01/sisvietnamvn_main/src/main/resources/config/liquibase/data/merge_doctors.sql', 'w', encoding='utf-8') as f:
f.write(plsql)
print("Generated merge_doctors.sql")
+129
View File
@@ -0,0 +1,129 @@
import xml.etree.ElementTree as ET
import csv
import sys
from bs4 import BeautifulSoup
try:
tree = ET.parse('/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/bnhvinakhoaquctsiscnth.WordPress.2026-07-28 (5).xml')
root = tree.getroot()
except Exception as e:
print(f"Error parsing XML: {e}")
sys.exit(1)
ns = {
'wp': 'http://wordpress.org/export/1.2/',
'content': 'http://purl.org/rss/1.0/modules/content/'
}
attachments = {}
for channel in root.findall('channel'):
for item in channel.findall('item'):
pt = item.find('wp:post_type', ns)
if pt is not None and pt.text == 'attachment':
post_id = item.find('wp:post_id', ns)
url = item.find('wp:attachment_url', ns)
if post_id is not None and url is not None:
attachments[post_id.text] = url.text
csv_mapping = {}
with open('/home/x79/sisvietnamvn_01/Media/HÌNH ẢNH BÁC SĨ NGỒI PHÒNG KHÁM/ALL_IMAGES/DANH SÁCH BÁC SĨ NGỒI PHÒNG KHÁM.csv', 'r', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
for row in reader:
name = row.get('Họ và tên', '').strip()
msnv = row.get('MSNV', '').strip()
if name:
csv_mapping[name.lower()] = msnv
def escape_sql(text):
if not text:
return "NULL"
return "'" + text.replace("'", "''") + "'"
def parse_html_content(html):
if not html:
return None, None, None
soup = BeautifulSoup(html, 'html.parser')
education = ""
experience = ""
achievements = ""
current_section = None
# We will just split it simply for now, but a more robust way:
# Just save the entire raw HTML into work_experience if we can't easily parse
return escape_sql(html), "NULL", "NULL"
sql_statements = []
# TRUNCATE TABLE sis_doctor CASCADE? We should probably just insert.
# But there might be old data. Let's just generate INSERTs.
doctor_id = 1000
for channel in root.findall('channel'):
for item in channel.findall('item'):
pt = item.find('wp:post_type', ns)
if pt is not None and pt.text == 'employee':
title_el = item.find('title')
raw_title = title_el.text.strip() if title_el is not None and title_el.text else ""
# Extract name and title (e.g. "TS.BS TRẦN CHÍ CƯỜNG" -> title="TS.BS", name="TRẦN CHÍ CƯỜNG")
parts = raw_title.split(' ', 1)
doctor_title = parts[0] if '.' in parts[0] else ''
doctor_name = parts[1] if doctor_title and len(parts) > 1 else raw_title
if not doctor_title:
doctor_name = raw_title
doctor_name_lower = doctor_name.lower()
msnv = ""
for csv_name, code in csv_mapping.items():
if csv_name in doctor_name_lower or doctor_name_lower in csv_name:
msnv = code
break
meta_dict = {}
for meta in item.findall('wp:postmeta', ns):
k = meta.find('wp:meta_key', ns)
v = meta.find('wp:meta_value', ns)
if k is not None and k.text and v is not None:
meta_dict[k.text] = v.text or ""
position = meta_dict.get('position', '')
gender = meta_dict.get('gender', '')
birthday = meta_dict.get('birthday', '')
phone = meta_dict.get('phone_number', '')
email = meta_dict.get('email', '')
address = meta_dict.get('address', '')
order = meta_dict.get('order', '0')
image_id = meta_dict.get('image', '')
avatar_url = attachments.get(image_id, '')
content_el = item.find('content:encoded', ns)
html_content = content_el.text if content_el is not None else ""
exp, edu, ach = parse_html_content(html_content)
if not order.isdigit():
order = '0'
# INSERT statement
# Use sequenceGenerator.nextval or just let JPA handle it? Since we insert via SQL, we use sequence_generator.nextval
sql = f"""
INSERT INTO sis_doctor (
id, name, title, avatar_url, doctor_code, active,
position, gender, birthday, phone_number, email, address, display_order,
work_experience, created_by, created_date, last_modified_by, last_modified_date
) VALUES (
{doctor_id}, {escape_sql(doctor_name)}, {escape_sql(doctor_title)}, {escape_sql(avatar_url)}, {escape_sql(msnv)}, 1,
{escape_sql(position)}, {escape_sql(gender)}, {escape_sql(birthday)}, {escape_sql(phone)}, {escape_sql(email)}, {escape_sql(address)}, {order},
{exp}, 'system', sysdate, 'system', sysdate
);"""
sql_statements.append(sql.strip())
doctor_id += 1
with open('/home/x79/sisvietnamvn_01/sisvietnamvn_main/import_doctors.sql', 'w', encoding='utf-8') as f:
f.write("DELETE FROM sis_doctor;\n")
f.write("\n".join(sql_statements))
f.write("\nCOMMIT;\n")
print("Generated import_doctors.sql")
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
import xml.etree.ElementTree as ET
tree = ET.parse('/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/bnhvinakhoaquctsiscnth.WordPress.2026-07-28 (5).xml')
root = tree.getroot()
ns = {
'wp': 'http://wordpress.org/export/1.2/',
'content': 'http://purl.org/rss/1.0/modules/content/',
'dc': 'http://purl.org/dc/elements/1.1/'
}
for channel in root.findall('channel'):
for item in channel.findall('item'):
post_type = item.find('wp:post_type', ns)
if post_type is None or post_type.text != 'doctor':
continue
print('Title:', item.find('title').text)
print('Post Type:', post_type.text)
for meta in item.findall('wp:postmeta', ns):
key = meta.find('wp:meta_key', ns)
val = meta.find('wp:meta_value', ns)
if key is not None and val is not None and key.text and not key.text.startswith('_'):
print(f' Meta: {key.text} = {str(val.text)[:100]}')
break
+1
View File
@@ -0,0 +1 @@
Post types: {'employee'}
Binary file not shown.
@@ -56,7 +56,7 @@ public class SecurityConfiguration {
"/manage/login", "/css/**", "/images/**", "/js/**", "/vendor/**", "/fonts/**", "/login-assets/**", "/UMass*/**", "/Undergraduate*/**",
"/favicon.ico", "/favicons/**", "/flex-finish/**", "/contact-us/**", "/uploads/**", "/upload/**", "/api/manage/snippets/**", "/page/**", "/news/article/**", "/post/**", "/error",
"/about-us", "/specialty", "/doctor", "/bac-si/**", "/service", "/health-library", "/news-and-events", "/patient-support", "/medical-expert", "/umcers", "/bidding", "/contact-us", "/theme-assets/**", "/dao-tao/**", "/chuyen-khoa", "/chuyen-khoa/**",
"/Đào tạo tại UMC_files/**")
"/Đào tạo tại UMC_files/**", "/dat-lich", "/dat-lich/**", "/api/public/doctor-schedule/**")
.permitAll()
.requestMatchers(HttpMethod.POST, "/api/manage/media/upload").permitAll()
.requestMatchers(HttpMethod.GET, "/swagger-ui/**", "/v3/api-docs/**").permitAll()
@@ -267,9 +267,9 @@ public class PageController {
java.util.List<com.sisvietnamvn.web.domain.Doctor> doctors = doctorService.findAll();
// Show all doctors regardless of active status (so inactive still show up)
// doctors = doctors.stream()
// .filter(d -> Boolean.TRUE.equals(d.getActive()))
// .collect(java.util.stream.Collectors.toList());
doctors = doctors.stream()
.filter(d -> Boolean.TRUE.equals(d.getActive()))
.collect(java.util.stream.Collectors.toList());
// Sort: 00001 and 00002 at the top
doctors.sort((d1, d2) -> {
@@ -411,6 +411,18 @@ public class PageController {
return "page";
}
@GetMapping("/dat-lich")
public String getDatLichPage(Model model) {
model.addAttribute("activeTheme", "umass");
model.addAttribute("cachedDoctors", doctorApiSyncService.getCachedDoctors());
return "pages/dat-lich";
}
@GetMapping("/api/public/doctor-schedule/{doctorCode}")
@org.springframework.web.bind.annotation.ResponseBody
public org.springframework.http.ResponseEntity<List<Map<String, Object>>> getPublicSchedule(@PathVariable String doctorCode) {
return getManualSchedule(doctorCode);
}
@GetMapping("/manage/doctor-scheduler")
public String getQuanLyLichKham(Model model) {
@@ -45,47 +45,6 @@ public class ManageDoctorController {
populateModel(model, new Doctor(), true);
return "manage/doctors/index";
}
@GetMapping("/import-csv")
@ResponseBody
public String importCsv() {
try {
String file = "/home/x79/sisvietnamvn_01/Media/HÌNH ẢNH BÁC SĨ NGỒI PHÒNG KHÁM/ALL_IMAGES/DANH SÁCH BÁC SĨ NGỒI PHÒNG KHÁM.csv";
java.util.List<String> lines = java.nio.file.Files.readAllLines(java.nio.file.Paths.get(file));
int updatedCount = 0;
for (int i = 1; i < lines.size(); i++) {
String line = lines.get(i);
String[] parts = line.split(",", -1);
if (parts.length >= 7) {
String doctorCode = parts[1].trim();
String specialtyName = parts[6].trim();
if (!doctorCode.isEmpty() && !specialtyName.isEmpty()) {
java.util.Optional<Doctor> docOpt = doctorService.findByDoctorCode(doctorCode);
if (docOpt.isPresent()) {
Doctor doctor = docOpt.get();
// Find or create specialty
java.util.List<com.sisvietnamvn.web.domain.Specialty> specs = specialtyService.findAll();
com.sisvietnamvn.web.domain.Specialty spec = specs.stream().filter(s -> s.getName().equalsIgnoreCase(specialtyName)).findFirst().orElse(null);
if (spec == null) {
spec = new com.sisvietnamvn.web.domain.Specialty();
spec.setName(specialtyName);
spec = specialtyService.save(spec);
}
doctor.setSpecialty(spec);
doctorService.save(doctor);
updatedCount++;
}
}
}
}
return "Import success! Updated " + updatedCount + " doctors.";
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
@PostMapping
public String createDoctor(@Valid @ModelAttribute("doctor") Doctor doctor,
@@ -60,6 +60,33 @@ public class Doctor extends AbstractAuditingEntity<Long> {
@Column(name = "active")
private Boolean active = true;
@Size(max = 255)
@Column(name = "position", length = 255)
private String position;
@Size(max = 20)
@Column(name = "gender", length = 20)
private String gender;
@Size(max = 20)
@Column(name = "birthday", length = 20)
private String birthday;
@Size(max = 50)
@Column(name = "phone_number", length = 50)
private String phoneNumber;
@Size(max = 100)
@Column(name = "email", length = 100)
private String email;
@Size(max = 255)
@Column(name = "address", length = 255)
private String address;
@Column(name = "display_order")
private Integer displayOrder;
// jhipster-needle-entity-add-field - JHipster will add fields here
// --- Getters and Setters ---
@@ -153,6 +180,62 @@ public class Doctor extends AbstractAuditingEntity<Long> {
this.active = active;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getDisplayOrder() {
return displayOrder;
}
public void setDisplayOrder(Integer displayOrder) {
this.displayOrder = displayOrder;
}
// --- End Getters and Setters ---
// --- equals, hashCode, toString ---
@@ -178,6 +261,9 @@ public class Doctor extends AbstractAuditingEntity<Long> {
", avatarUrl='" + getAvatarUrl() + "'" +
", bookingUrl='" + getBookingUrl() + "'" +
", doctorCode='" + getDoctorCode() + "'" +
", position='" + getPosition() + "'" +
", gender='" + getGender() + "'" +
", email='" + getEmail() + "'" +
"}";
}
}
@@ -1,13 +1,27 @@
package com.sisvietnamvn.web.domain;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.io.Serial;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* A Post entity representing a blog/news article.
* Maps to the "sis_post" database table.
@@ -227,4 +241,9 @@ public class Post extends AbstractAuditingEntity<Long> {
", status='" + getStatus() + "'" +
"}";
}
@jakarta.persistence.Transient
public String getImageUrl() {
return this.featuredImage;
}
}
@@ -131,44 +131,72 @@ public class SwiperSliderPlugin {
private Optional<SwiperSliderAdminController.SliderGroup> findGroupOrSeed(String slug) {
String json = settingService.getValue(SETTING_KEY, "[]");
List<SwiperSliderAdminController.SliderGroup> groups = parseGroups(json);
final String searchSlug = ("dao-tao-01".equals(slug)) ? "gallery" : slug;
Optional<SwiperSliderAdminController.SliderGroup> groupOpt = groups.stream()
.filter(g -> slug.equals(g.slug()))
.filter(g -> slug.equals(g.slug()) || searchSlug.equals(g.slug()))
.findFirst();
// If UMC_files is present, we replace all data with defaults (only happens once during migration)
if (json.contains("UMC_files")) {
// If UMC_files is present or slug not found, re-seed full data
if (groupOpt.isEmpty() || json.contains("UMC_files")) {
json = forceSeedAllData();
groups = parseGroups(json);
groupOpt = groups.stream()
.filter(g -> slug.equals(g.slug()))
.filter(g -> slug.equals(g.slug()) || searchSlug.equals(g.slug()))
.findFirst();
}
return groupOpt;
}
private String forceSeedAllData() {
String sampleData = "[{\"slug\":\"gallery\",\"name\":\"Gallery Đào Tạo\",\"items\":[" +
private static final String DEFAULT_SLIDER_DATA = "[" +
"{\"slug\":\"dao-tao-01\",\"name\":\"Gallery Đào Tạo\",\"items\":[" +
"{\"title\":\"Khóa đào tạo 1\",\"imageUrl\":\"/images/dao-tao/image-6.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 2\",\"imageUrl\":\"/images/dao-tao/image-5.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 3\",\"imageUrl\":\"/images/dao-tao/image-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"{\"title\":\"Khóa đào tạo 3\",\"imageUrl\":\"/images/dao-tao/image-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 4\",\"imageUrl\":\"/images/dao-tao/image-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 5\",\"imageUrl\":\"/images/dao-tao/image-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 6\",\"imageUrl\":\"/images/dao-tao/image-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"]},{\"slug\":\"gallery\",\"name\":\"Gallery Đào Tạo\",\"items\":[" +
"{\"title\":\"Khóa đào tạo 1\",\"imageUrl\":\"/images/dao-tao/image-6.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 2\",\"imageUrl\":\"/images/dao-tao/image-5.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 3\",\"imageUrl\":\"/images/dao-tao/image-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 4\",\"imageUrl\":\"/images/dao-tao/image-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 5\",\"imageUrl\":\"/images/dao-tao/image-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Khóa đào tạo 6\",\"imageUrl\":\"/images/dao-tao/image-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"]},{\"slug\":\"chung-chi\",\"name\":\"Cấp chứng chỉ\",\"items\":[" +
"{\"title\":\"Lớp chứng chỉ 1\",\"imageUrl\":\"/images/dao-tao/image-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Lớp chứng chỉ 2\",\"imageUrl\":\"/images/dao-tao/image-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Lớp chứng chỉ 3\",\"imageUrl\":\"/images/dao-tao/image-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"{\"title\":\"Lớp chứng chỉ 3\",\"imageUrl\":\"/images/dao-tao/image-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Lớp chứng chỉ 4\",\"imageUrl\":\"/images/dao-tao/training-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Lớp chứng chỉ 5\",\"imageUrl\":\"/images/dao-tao/training-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Lớp chứng chỉ 6\",\"imageUrl\":\"/images/dao-tao/training-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"]},{\"slug\":\"giay-chung-nhan\",\"name\":\"Cấp giấy chứng nhận\",\"items\":[" +
"{\"title\":\"Giấy chứng nhận 1\",\"imageUrl\":\"/images/dao-tao/training-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Giấy chứng nhận 2\",\"imageUrl\":\"/images/dao-tao/training-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"{\"title\":\"Giấy chứng nhận 2\",\"imageUrl\":\"/images/dao-tao/training-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Giấy chứng nhận 3\",\"imageUrl\":\"/images/dao-tao/training-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Giấy chứng nhận 4\",\"imageUrl\":\"/images/dao-tao/training-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Giấy chứng nhận 5\",\"imageUrl\":\"/images/dao-tao/image-5.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Giấy chứng nhận 6\",\"imageUrl\":\"/images/dao-tao/image-6.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"]},{\"slug\":\"xac-nhan-thuc-hanh\",\"name\":\"Đào tạo cấp giấy xác nhận quá trình thực hành\",\"items\":[" +
"{\"title\":\"Xác nhận thực hành 1\",\"imageUrl\":\"/images/dao-tao/PTNS2707.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Xác nhận thực hành 2\",\"imageUrl\":\"/images/dao-tao/training-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"{\"title\":\"Xác nhận thực hành 2\",\"imageUrl\":\"/images/dao-tao/training-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Xác nhận thực hành 3\",\"imageUrl\":\"/images/dao-tao/training-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Xác nhận thực hành 4\",\"imageUrl\":\"/images/dao-tao/training-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Xác nhận thực hành 5\",\"imageUrl\":\"/images/dao-tao/image-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Xác nhận thực hành 6\",\"imageUrl\":\"/images/dao-tao/image-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"]},{\"slug\":\"hinh-anh-thuc-hanh\",\"name\":\"Hình ảnh thực hành lâm sàng\",\"items\":[" +
"{\"title\":\"Thực hành lâm sàng 1\",\"imageUrl\":\"/images/dao-tao/training-1.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Thực hành lâm sàng 2\",\"imageUrl\":\"/images/dao-tao/training-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"{\"title\":\"Thực hành lâm sàng 2\",\"imageUrl\":\"/images/dao-tao/training-2.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Thực hành lâm sàng 3\",\"imageUrl\":\"/images/dao-tao/training-3.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Thực hành lâm sàng 4\",\"imageUrl\":\"/images/dao-tao/training-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Thực hành lâm sàng 5\",\"imageUrl\":\"/images/dao-tao/image-4.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}," +
"{\"title\":\"Thực hành lâm sàng 6\",\"imageUrl\":\"/images/dao-tao/image-5.jpeg\",\"linkUrl\":\"\",\"description\":\"\"}" +
"]}]";
settingService.setValue(SETTING_KEY, sampleData);
LOG.info("Forced seeding of default swiper slider data for all 5 training sliders");
return sampleData;
private String forceSeedAllData() {
settingService.setValue(SETTING_KEY, DEFAULT_SLIDER_DATA);
LOG.info("Forced seeding of default swiper slider data for all training sliders");
return DEFAULT_SLIDER_DATA;
}
private List<SwiperSliderAdminController.SliderGroup> parseGroups(String json) {
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728163000-1" author="antigravity">
<addColumn tableName="sis_doctor">
<column name="position" type="varchar(255)">
<constraints nullable="true" />
</column>
<column name="gender" type="varchar(20)">
<constraints nullable="true" />
</column>
<column name="birthday" type="varchar(20)">
<constraints nullable="true" />
</column>
<column name="phone_number" type="varchar(50)">
<constraints nullable="true" />
</column>
<column name="email" type="varchar(100)">
<constraints nullable="true" />
</column>
<column name="address" type="varchar(255)">
<constraints nullable="true" />
</column>
<column name="display_order" type="int">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728164000-1" author="antigravity">
<sqlFile path="config/liquibase/data/import_doctors.sql"
relativeToChangelogFile="false"
encoding="utf8"
splitStatements="true"
stripComments="true"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728165000-1" author="antigravity">
<sqlFile path="config/liquibase/data/map_specialties.sql"
relativeToChangelogFile="false"
encoding="utf8"
splitStatements="false"
stripComments="true"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728166000-1" author="antigravity">
<sqlFile path="config/liquibase/data/update_active_doctors.sql"
relativeToChangelogFile="false"
encoding="utf8"
splitStatements="true"
stripComments="true"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728171000-1" author="antigravity">
<sqlFile path="config/liquibase/data/merge_doctors.sql"
relativeToChangelogFile="false"
encoding="utf8"
splitStatements="true"
stripComments="true"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728172000-1" author="antigravity">
<sqlFile path="config/liquibase/data/merge_doctors.sql"
relativeToChangelogFile="false"
encoding="utf8"
splitStatements="true"
stripComments="true"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.6.xsd">
<changeSet id="20260728173000-1" author="antigravity">
<sqlFile path="config/liquibase/data/merge_doctors.sql"
relativeToChangelogFile="false"
encoding="utf8"
splitStatements="false"
stripComments="true"/>
</changeSet>
</databaseChangeLog>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
BEGIN
COMMIT;
END;
/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
UPDATE sis_doctor SET active = 0 WHERE doctor_code IS NULL OR doctor_code = '';
UPDATE sis_doctor SET active = 1 WHERE doctor_code IS NOT NULL AND doctor_code != '';
COMMIT;
@@ -52,4 +52,9 @@
<include file="config/liquibase/changelog/20260724151500_alter_setting_value_to_clob.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728140500_add_contact_fields_to_page.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728160000_add_specialty_category.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728163000_add_xml_fields_to_doctor.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728164000_import_doctors_data.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728165000_map_specialties.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728166000_update_active_doctors.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260728173000_fix_xml_specialties.xml" relativeToChangelogFile="false"/>
</databaseChangeLog>
@@ -81,31 +81,23 @@
<div style="-webkit-text-stroke-width:0px;border-style:solid;border-width:0px;box-sizing:border-box;color:rgb(51, 51, 51);font-family:Roboto, ui-sans-serif, system-ui, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;;font-size:16px;font-style:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-weight:400;letter-spacing:normal;margin:0px;orphans:2;padding:0px;text-align:justify;text-decoration-color:initial;text-decoration-style:initial;text-decoration-thickness:initial;text-indent:0px;text-transform:none;white-space:normal;widows:2;word-spacing:0px;">Phòng KHĐT cùng Đơn vị lập kế hoạch, dự trù kinh phí cho các lớp đào tạo với các hình thức như đào tạo y khoa liên tục (CME), hội nghị, hội thảo, lớp học, … Những chương trình này có thể tổ chức tại Bệnh viện hoặc tổ chức tại các địa điểm bên ngoài tùy theo nhu cầu thực tế để đưa hoạt động đào tạo thực sự trở thành một mũi nhọn, một đặc trưng của bệnh viện.</div>
</div>
<div>
<div class="relative md:flex md:items-center md:gap-x-2">
<button class="btn-navigation md:size-[42px] size-[32px] flex-shrink-0 items-center justify-center rounded-full btn-navigation-gallery-prev md:flex hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up size-4 -rotate-90">
<path d="m18 15-6-6-6 6"></path>
</svg>
</button>
<div class="swiper swiper-gallery w-full">
<div class="swiper-wrapper">
<th:block th:utext="${hookManager.applyFilters('swiper_slider_items', '', 'gallery')}"></th:block>
</div>
<div class="relative md:flex md:items-center md:gap-x-2">
<button class="btn-navigation md:size-[42px] size-[32px] flex-shrink-0 items-center justify-center rounded-full btn-navigation-dao-tao-01-prev md:flex hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up size-4 -rotate-90">
<path d="m18 15-6-6-6 6"></path>
</svg>
</button>
<div class="swiper swiper-dao-tao-01 w-full">
<div class="swiper-wrapper">
<th:block th:utext="${hookManager.applyFilters('swiper_slider_items', '', 'dao-tao-01')}"></th:block>
</div>
</div>
<button class="btn-navigation md:size-[42px] size-[32px] flex-shrink-0 items-center justify-center rounded-full btn-navigation-gallery-next md:flex hidden">
<button class="btn-navigation md:size-[42px] size-[32px] flex-shrink-0 items-center justify-center rounded-full btn-navigation-dao-tao-01-next md:flex hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down size-4 -rotate-90">
<path d="m6 9 6 6 6-6"></path>
</svg>
</button>
<div class="swiper-pagination swiper-pagination-training-gallery space-x-[13px] !relative [&amp;_.swiper-pagination-bullet-active]:!bg-primary-600 [&amp;_.swiper-pagination-bullet-active]:!opacity-100 [&amp;_.swiper-pagination-bullet-active]:before:opacity-100 mt-6 flex justify-center [&amp;_.swiper-pagination-bullet]:!relative [&amp;_.swiper-pagination-bullet]:before:content-[&#39;&#39;] [&amp;_.swiper-pagination-bullet]:before:size-[14px] [&amp;_.swiper-pagination-bullet]:before:bg-transparent [&amp;_.swiper-pagination-bullet]:before:rounded-full [&amp;_.swiper-pagination-bullet]:before:border [&amp;_.swiper-pagination-bullet]:before:border-primary-600 md:[&amp;_.swiper-pagination-bullet]:before:top-[-3px] [&amp;_.swiper-pagination-bullet]:before:top-[-3px] [&amp;_.swiper-pagination-bullet]:before:left-[-2.7px] [&amp;_.swiper-pagination-bullet]:before:absolute [&amp;_.swiper-pagination-bullet]:before:block [&amp;_.swiper-pagination-bullet]:before:opacity-0 [&amp;_.swiper-pagination-bullet]:!size-[8px] [&amp;_.swiper-pagination-bullet]:!bg-gray-600 md:hidden swiper-pagination-clickable swiper-pagination-bullets swiper-pagination-horizontal">
<span class="swiper-pagination-bullet swiper-pagination-bullet-active"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
</div>
</div>
</div>
</div>
</section>
<section class="xl:py-12 md:py-8 py-6 bg-[#f6f6f6] xl:space-y-12 md:space-y-8 space-y-6">
@@ -116,12 +108,12 @@
</div>
<div class="flex items-center gap-4">
<div class="flex md:gap-2 gap-1">
<button class="btn-navigation-khoa-hoc-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-khoa-hoc-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up size-4 -rotate-90">
<path d="m18 15-6-6-6 6"></path>
</svg>
</button>
<button class="btn-navigation-khoa-hoc-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-khoa-hoc-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down size-4 -rotate-90">
<path d="m6 9 6 6 6-6"></path>
</svg>
@@ -131,22 +123,39 @@
</div>
<div class="relative max-md:px-4 md:mb-6 mb-4 lg:mb-8">
<div class="swiper swiper-khoa-hoc w-full">
<div class="swiper-wrapper">
<!-- 1. ĐÃ XÓA gap-3 -->
<div class="swiper-wrapper" style="height: 450px;">
<th:block th:each="post : ${daoTaoPosts}">
<div class="swiper-slide h-auto max-w-[288px] mr-2 lg:!mr-3 xl:!mr-4" style="width: 288px; max-width: 100%;">
<a th:href="@{/dao-tao/{slug}(slug=${post.slug})}" class="group h-full flex flex-col items-start gap-y-4 lg:gap-y-5 rounded-2xl bg-white p-4 pb-5 shadow-sm transition-shadow hover:shadow-md border border-transparent hover:border-primary-100">
<div class="relative w-full aspect-square overflow-hidden rounded-xl">
<img th:src="${post.imageUrl != null and !#strings.isEmpty(post.imageUrl) ? post.imageUrl : '/images/dao-tao/training-1.jpeg'}" th:alt="${post.title}" class="absolute inset-0 size-full object-cover transition-transform duration-500 group-hover:scale-105">
<!-- 2. Dùng h-auto để Swiper tự động stretch các card cao bằng nhau -->
<div class="swiper-slide h-auto" style="width: 288px; max-width: 100%; margin: 5px; background-color: var(--color-white);">
<!-- Thẻ a (card) đã set h-full để giãn kịch trần -->
<a th:href="@{/dao-tao/{slug}(slug=${post.slug})}" class="group h-full flex flex-col items-start">
<!-- 3. Đã thêm overflow-hidden vào khung ảnh -->
<div class="w-full rounded-xl mb-3">
<img th:src="${post.imageUrl != null and !#strings.isEmpty(post.imageUrl) ? post.imageUrl : '/images/dao-tao/training-1.jpeg'}"
th:alt="${post.title}"
class="size-full aspect-square object-cover transition-transform duration-500 group-hover:scale-105">
</div>
<div class="flex flex-1 flex-col justify-between w-full">
<h3 class="heading-5 text-gray-900 group-hover:text-primary-600 transition-colors line-clamp-3" th:text="${post.title}">Tiêu đề khóa học</h3>
<!-- 4. Gom Tiêu đề và Trích dẫn vào chung 1 thẻ flex-1 -->
<div class="flex flex-1 flex-col justify-start w-full gap-y-2">
<h3 class="heading-5 text-gray-900 group-hover:text-primary-600 transition-colors line-clamp-3"
th:text="${post.title}">Tiêu đề khóa học</h3>
<!-- Đoạn text trích dẫn -->
<p class="body-3 text-gray-600 line-clamp-3"
th:text="${post.excerpt}">Giới thiệu ngắn gọn</p>
</div>
</a>
</div>
</th:block>
</div>
</div>
</div>
</div>
<div class="lg:container max-lg:pl-5 max-md:px-0">
<div class="flex justify-between items-end md:mb-6 mb-4 lg:mb-8 max-lg:pr-5 max-md:px-4">
@@ -155,12 +164,12 @@
</div>
<div class="flex items-center gap-4">
<div class="flex md:gap-2 gap-1">
<button class="btn-navigation-cap-chung-chi-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-cap-chung-chi-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up size-4 -rotate-90">
<path d="m18 15-6-6-6 6"></path>
</svg>
</button>
<button class="btn-navigation-cap-chung-chi-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-cap-chung-chi-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down size-4 -rotate-90">
<path d="m6 9 6 6 6-6"></path>
</svg>
@@ -183,12 +192,12 @@
</div>
<div class="flex items-center gap-4">
<div class="flex md:gap-2 gap-1">
<button class="btn-navigation-cap-giay-chung-nhan-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-cap-giay-chung-nhan-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up size-4 -rotate-90">
<path d="m18 15-6-6-6 6"></path>
</svg>
</button>
<button class="btn-navigation-cap-giay-chung-nhan-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-cap-giay-chung-nhan-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down size-4 -rotate-90">
<path d="m6 9 6 6 6-6"></path>
</svg>
@@ -211,12 +220,12 @@
</div>
<div class="flex items-center gap-4">
<div class="flex md:gap-2 gap-1">
<button class="btn-navigation-dao-tao-cap-giay-xac-nhan-qua-trinh-thuc-hanh-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-dao-tao-cap-giay-xac-nhan-qua-trinh-thuc-hanh-prev md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up size-4 -rotate-90">
<path d="m18 15-6-6-6 6"></path>
</svg>
</button>
<button class="btn-navigation-dao-tao-cap-giay-xac-nhan-qua-trinh-thuc-hanh-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group swiper-button-disabled swiper-button-lock" disabled="">
<button class="btn-navigation-dao-tao-cap-giay-xac-nhan-qua-trinh-thuc-hanh-next md:size-10 size-8 flex items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down size-4 -rotate-90">
<path d="m6 9 6 6 6-6"></path>
</svg>
@@ -301,10 +310,6 @@
</div>
</div>
</section>
<!--$-->
<!--/$-->
<!--$-->
<!--/$-->
</main>
<div class="fixed bottom-6 right-6 z-[49] flex flex-col gap-3 items-end">
<a href="tel:19002827" aria-label="Gọi điện thoại" class="flex h-10 w-10 items-center justify-center rounded-full bg-primary-700 shadow-lg transition-colors hover:bg-primary-800" style="opacity: 1; transform: none; transform-origin: 50% 50% 0px;">
@@ -318,358 +323,7 @@
</svg>
</button>
</div>
<!--/$-->
<next-route-announcer style="position: absolute;">
<template shadowrootmode="open">
<div aria-live="assertive" id="__next-route-announcer__" role="alert" style="position: absolute; border: 0px; height: 1px; margin: -1px; padding: 0px; width: 1px; clip: rect(0px, 0px, 0px, 0px); overflow: hidden; white-space: nowrap; overflow-wrap: normal;"></div>
</template>
</next-route-announcer>
<div id="easychatgpt-widget" data-ecg-initialized="true" style="--ui-logo: https://assets.geteasy.ai/workspaces/2353/bots/2770/public/0fd2dbe4-63bf-4d8e-871f-ab903f800fca.png; --ui-name: Trợ lý AI của UMC; --ui-size: medium; --ui-theme: light; --ui-alignment-offset-mobile-vertical: 125px; --ui-alignment-offset-mobile-horizontal: 15px; --ui-alignment-offset-desktop-vertical: 130px; --ui-alignment-offset-desktop-horizontal: 20px; --ui-alignment-position: right; --ui-icon-image: https://assets.geteasy.ai/workspaces/2353/bots/2770/public/5026d034-feea-46db-a0c1-60c078ef5d5d.png; --ui-bubble_chat-offset-mobile-size: small; --ui-bubble_chat-offset-mobile-custom: 60px; --ui-bubble_chat-offset-desktop-size: small; --ui-bubble_chat-offset-desktop-custom: 60px; --ui-primary-color: rgb(8, 82, 158); --ui-chat-button-icon: &lt;svg width=&quot;30&quot; height=&quot;40&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; class=&quot;block z-10 text-white align-middle border-gray-200&quot;&gt;&lt;path d=&quot;m3 21 1.9-5.7a8.5 8.5 0 1 1 3.8 3.8z&quot; class=&quot;border-gray-200&quot;&gt;&lt;/path&gt;&lt;/svg&gt;; --ui-automatically-expand: false; --ui-show-assistant-avatar: true;">
<template shadowrootmode="open">
<div data-v-app="">
<div workspaceuuid="d734fb80-4d39-4fd3-8b8c-d6af95ad4489">
<div class="chat-widget position-right btn-size-desktop-small btn-size-mobile-small" style="display: none; --size-desktop-custom: 60px; --size-mobile-custom: 60px; --z-index-chatview: 9999999999; --z-index-bubblechat: 999999990;">
<div class="w-screen grow sm:w-[425px] sm:h-[720px] sm:max-h-[calc(100vh-6rem)] z-[var(--z-index-chatview)] overflow-hidden ecg-chat-view sm:rounded-3xl box-shadow-md max-sm:border-t-0 max-sm:border-x-0 fixed max-sm:right-0 sm:right-4 duration-500 opacity-0 invisible translate-y-[150%] h-dvh rounded-none max-sm:top-0">
<div id="ei-chat-container" class="flex flex-col h-full w-full bg-gray-50 h-full w-full">
<div class="flex-1 overflow-hidden h-full">
<!---->
<div id="chat-view" class="flex flex-col overflow-hidden bg-white h-full w-full pb-1">
<div class="relative ecg-widget-header">
<div class="flex items-center justify-between p-3.5 text-white bg-primary">
<div class="flex items-center gap-2">
<button class="p-1.5 rounded-lg hover:bg-white/10 text-white" aria-label="Quay lại">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="20" class="lucide lucide-chevron-left-icon">
<path d="m15 18-6-6 6-6"></path>
</svg>
</button>
<!---->
<h2 class="text-base font-medium tracking-tight">Trợ lý AI của UMC</h2>
</div>
<div class="flex gap-1.5">
<!---->
<div class="flex items-center justify-center rounded-lg size-8 bg-white/10 hover:bg-white/20 cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="14" class="lucide lucide-refresh-ccw-icon">
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
<path d="M3 3v5h5"></path>
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"></path>
<path d="M16 16h5v5"></path>
</svg>
</div>
<button aria-label="eac-close-chat" class="size-8 rounded-lg flex items-center justify-center bg-white/10 hover:bg-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="16" class="lucide lucide-minus-icon">
<path d="M5 12h14"></path>
</svg>
</button>
</div>
</div>
</div>
<div class="messages-wrapper">
<div class="chat-banners block chat-content-item">
<div class="embla-wrapper">
<div class="embla">
<div class="embla__container" style="transform: translate3d(0px, 0px, 0px);">
<div class="embla__slide w-full h-[150px]">
<div target="_blank" class="block w-full h-full">
<img src="https://assets.geteasy.ai/workspaces/2353/bots/2770/public/fabf8d12-7b48-4257-a754-78288d866339.png" alt="ecg-banner" class="object-cover w-full h-full">
</div>
</div>
</div>
</div>
<!---->
</div>
<button class="close-banner-btn">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-3">
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
</div>
<div class="flex flex-col flex-grow px-2 pt-4 space-y-4 duration-100 lg:px-4 chat-content-item">
<div class="space-y-3 mb-4">
<div class="messages-group message-assistant">
<span class="relative flex flex-shrink-0 overflow-hidden bg-white size-8 message-avatar opacity-0 rounded">
<img src="https://assets.geteasy.ai/workspaces/2353/bots/2770/public/0fd2dbe4-63bf-4d8e-871f-ab903f800fca.png" alt="Trợ lý AI của UMC" class="object-cover w-full h-full">
</span>
<div class="messages">
<div class="relative flex-1 group w-fit max-w-[92%]">
<!---->
<div class="relative w-full mr-auto">
<div class="message message-assistant">
<div class="relative max-w-full w-fit">
<div class="relative px-4 py-3.5 min-w-11 bg-gray-100 rounded-xl prose-sm prose max-sm:select-none">
<p>Xin chào, Em là trợ lý ảo của UMC 👋</p>
</div>
<button type="button" aria-label="Trả lời" title="Trả lời" class="reply-btn opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity duration-200 flex items-center justify-center size-6 rounded-full bg-gray-50 shadow-sm text-gray-500 hover:bg-gray-100 hover:text-gray-600 absolute top-1/2 -translate-y-1/2 -right-7">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-quote-icon size-3">
<path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"></path>
<path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"></path>
</svg>
</button>
<div class="absolute right-1.5 -bottom-3 opacity-0 md:group-hover:opacity-100 transition-opacity duration-300">
<div data-v-76058769="" class="relative reaction-container">
<div data-v-76058769="" class="flex items-center group-reactions">
<!---->
<button data-v-76058769="" class="reaction-button flex items-center justify-center size-[26px] border border-gray-100 shadow-sm rounded-full bg-white select-none transition-[transform,filter,background-color] duration-[150ms,200ms,200ms] ease-[cubic-bezier(0.175,0.885,0.32,1.275),ease,ease] hover:scale-110 active:scale-95 will-change-transform-opacity grayscale text-sm">
<svg data-v-76058769="" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="14" class="lucide lucide-heart-icon text-gray-500">
<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"></path>
</svg>
</button>
</div>
<div data-v-76058769="" class="absolute -top-2 z-10 -translate-y-full emoji-picker sm:top-0 origin-center-bottom will-change-[transform,opacity] translate-x-0 -right-0 sm:-right-3">
<div data-v-76058769="" class="flex justify-center items-center px-2 py-0.5 rounded-3xl select-none bg-white/90" style="box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;">
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0s;">
<span data-v-76058769="" class="leading-[100%] text-base sm:text-sm translate-y-[1px]">❤️</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Yêu thích</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.03s;">
<span data-v-76058769="" class="leading-[100%] text-base sm:text-sm translate-y-[1px]">👍</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Thích</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.06s;">
<span data-v-76058769="" class="leading-[100%] text-lg sm:text-base">🤣</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Haha</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.09s;">
<span data-v-76058769="" class="leading-[100%] text-lg sm:text-base">😮</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Wow</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.12s;">
<span data-v-76058769="" class="leading-[100%] text-lg sm:text-base">😭</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Buồn</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.15s;">
<span data-v-76058769="" class="leading-[100%] text-base sm:text-sm translate-y-[1px]">👎</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Không thích</div>
</button>
<!---->
</div>
</div>
<!---->
</div>
</div>
</div>
<!---->
</div>
</div>
<!---->
</div>
</div>
</div>
<div id="message-1" class="messages-group message-assistant !mt-2">
<span class="relative flex flex-shrink-0 overflow-hidden bg-white size-8 message-avatar opacity-100 rounded">
<img src="https://assets.geteasy.ai/workspaces/2353/bots/2770/public/0fd2dbe4-63bf-4d8e-871f-ab903f800fca.png" alt="Trợ lý AI của UMC" class="object-cover w-full h-full">
</span>
<div class="messages">
<div class="relative flex-1 group w-fit max-w-[92%]">
<!---->
<div class="relative w-full mr-auto">
<div class="message message-assistant">
<div class="relative max-w-full w-fit">
<div class="relative px-4 py-3.5 min-w-11 bg-gray-100 rounded-xl prose-sm prose max-sm:select-none">
<p>Em rất sẵn lòng hỗ trợ Anh/Chị 😊</p>
</div>
<button type="button" aria-label="Trả lời" title="Trả lời" class="reply-btn opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity duration-200 flex items-center justify-center size-6 rounded-full bg-gray-50 shadow-sm text-gray-500 hover:bg-gray-100 hover:text-gray-600 absolute top-1/2 -translate-y-1/2 -right-7">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-quote-icon size-3">
<path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"></path>
<path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"></path>
</svg>
</button>
<div class="absolute right-1.5 -bottom-3 opacity-100">
<div data-v-76058769="" class="relative reaction-container">
<div data-v-76058769="" class="flex items-center group-reactions">
<!---->
<button data-v-76058769="" class="reaction-button flex items-center justify-center size-[26px] border border-gray-100 shadow-sm rounded-full bg-white select-none transition-[transform,filter,background-color] duration-[150ms,200ms,200ms] ease-[cubic-bezier(0.175,0.885,0.32,1.275),ease,ease] hover:scale-110 active:scale-95 will-change-transform-opacity grayscale text-sm">
<svg data-v-76058769="" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="14" class="lucide lucide-heart-icon text-gray-500">
<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"></path>
</svg>
</button>
</div>
<div data-v-76058769="" class="absolute -top-2 z-10 -translate-y-full emoji-picker sm:top-0 origin-center-bottom will-change-[transform,opacity] translate-x-[46%] right-0">
<div data-v-76058769="" class="flex justify-center items-center px-2 py-0.5 rounded-3xl select-none bg-white/90" style="box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;">
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0s;">
<span data-v-76058769="" class="leading-[100%] text-base sm:text-sm translate-y-[1px]">❤️</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Yêu thích</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.03s;">
<span data-v-76058769="" class="leading-[100%] text-base sm:text-sm translate-y-[1px]">👍</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Thích</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.06s;">
<span data-v-76058769="" class="leading-[100%] text-lg sm:text-base">🤣</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Haha</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.09s;">
<span data-v-76058769="" class="leading-[100%] text-lg sm:text-base">😮</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Wow</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.12s;">
<span data-v-76058769="" class="leading-[100%] text-lg sm:text-base">😭</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Buồn</div>
</button>
<button data-v-76058769="" class="emoji-btn bg-transparent border-none cursor-pointer flex items-center justify-center size-[28px] sm:size-[26px] rounded-3xl hover:bg-gray-100 hover:scale-115 relative transition-[transform,background-color] duration-150 ease-in-out will-change-transform" style="--delay: 0.15s;">
<span data-v-76058769="" class="leading-[100%] text-base sm:text-sm translate-y-[1px]">👎</span>
<div data-v-76058769="" class="custom-tooltip absolute -top-[25px] left-1/2 -translate-x-1/2 scale-[0.8] bg-black/75 text-white text-[10px] px-1.5 py-0.5 rounded whitespace-nowrap opacity-0 pointer-events-none transition-[opacity,transform] duration-200 ease-in-out z-[60] hidden hover-hover:group-hover/emoji:opacity-100 hover-hover:group-hover/emoji:scale-100 after:content-[&#39;&#39;] after:absolute after:top-full after:left-1/2 after:-ml-1 after:border-4 after:border-solid after:border-t-black/75 after:border-x-transparent after:border-b-transparent">Không thích</div>
</button>
<!---->
</div>
</div>
<!---->
</div>
</div>
</div>
<!---->
</div>
</div>
<!---->
</div>
</div>
</div>
<!---->
<!---->
</div>
</div>
<div class="px-4 duration-200 pb-0">
<div class="flex flex-col items-end py-4 space-y-2">
<div class="quick-prompt-tag active">Giới thiệu về bệnh viện</div>
<div class="quick-prompt-tag active">Đặt lịch khám</div>
<div class="quick-prompt-tag active">Thủ tục khám bệnh bảo hiểm y tế</div>
</div>
</div>
</div>
<div class="relative">
<!---->
<!---->
<div class="relative z-30 bg-white">
<div class="w-full px-3 pt-1">
<div class="border border-gray-200 rounded-2xl">
<!---->
<!---->
<!---->
<div class="relative flex w-full">
<div class="flex absolute top-1/2 -translate-y-1/2 left-1.5 z-10 items-center quick-prompts">
<button type="button" class="flex items-center justify-center transition-colors rounded-full size-8 overflow-hidden hover:bg-gray-50 bg-white" title="Gợi ý nhanh">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="18" class="lucide lucide-menu-icon text-gray-500">
<line x1="4" x2="20" y1="12" y2="12"></line>
<line x1="4" x2="20" y1="6" y2="6"></line>
<line x1="4" x2="20" y1="18" y2="18"></line>
</svg>
</button>
<!---->
</div>
<textarea id="eac-chat-input" class="resize-none rounded-3xl input" placeholder="Nhập tin nhắn..." rows="1" style="height: 44px;"></textarea>
<div class="absolute z-10 flex items-center duration-200 top-1/2 -translate-y-1/2 right-1">
<!---->
<div class="w-fit relative group">
<input type="file" class="hidden" accept=".pdf,.doc,.docx,.xls,.xlsx,.csv,.jpg,.jpeg,.png,.webp,.bmp,.tiff,.tif,.heic,.avif,.mp3,.wav,.ogg,.m4a" multiple="">
<button type="button" class="size-9 flex items-center justify-center rounded-full sm:hover:bg-gray-100 cursor-pointer text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="18" class="lucide lucide-paperclip-icon">
<path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"></path>
</svg>
</button>
<!---->
</div>
<button type="button" class="bg-primary text-white sm:hover:opacity-80 flex items-center justify-center ml-1.5 duration-200 rounded-full size-9">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" size="16" class="lucide lucide-send-horizontal-icon">
<path d="m3 3 3 9-3 9 19-9Z"></path>
<path d="M6 12h16"></path>
</svg>
</button>
</div>
</div>
<!---->
</div>
</div>
</div>
</div>
<div class="drawer-form" tabindex="-1"></div>
<div class="drawer-form" tabindex="-1"></div>
<!---->
<div class="drawer-overlay-custom"></div>
<!---->
<div class="drawer-overlay-custom"></div>
<!---->
<div class="drawer-overlay-custom"></div>
<!---->
<div class="drawer-overlay-custom"></div>
<!---->
</div>
<!---->
<!---->
</div>
<!---->
<div class="flex items-center justify-center w-full py-1 flex-row bg-white">
<!---->
<a href="https://geteasy.ai/" target="_blank" rel="nofollow noreferrer" class="text-[10px] text-gray-500 font-medium flex items-center space-x-1 px-2 pb-0.5">
<span>Powered by</span>
<div class="flex items-center space-x-1">
<svg width="20" height="6.25" viewBox="0 0 32 10" fill="none" xmlns="http://www.w3.org/2000/svg" class="mt-px">
<path d="M29.0312 2.39062V2.39062C21.8342 9.58768 10.1655 9.58768 2.9684 2.39062V2.39062" stroke="url(#paint0_linear_950_398)" stroke-width="3" stroke-linecap="square"></path>
<defs>
<lineargradient id="paint0_linear_950_398" x1="9.48412" y1="8.90634" x2="22.5155" y2="-4.12509" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#3A087F"></stop>
<stop offset="40%" stop-color="#A535EB"></stop>
<stop offset="70%" stop-color="#FCA63E"></stop>
<stop offset="88%" stop-color="#54D5A4"></stop>
<stop offset="100%" stop-color="#1B8AF7"></stop>
</lineargradient>
</defs>
</svg>
<span class="font-bold text-gray-900">Easy AI</span>
</div>
</a>
</div>
</div>
</div>
<div class="z-[var(--z-index-bubblechat)] chat-button-group fixed bottom-[var(--ui-alignment-offset-vertical)] space-y-2">
<div>
<div class="flex-col space-y-2 sm:space-y-3 group items-end hidden sm:flex">
<div class="pr-0.5 duration-500 group-hover:block inner-close hidden">
<button class="flex overflow-hidden relative justify-center items-center w-6 h-6 md:w-5 md:h-5 rounded-full hover:opacity-70">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="relative z-10 w-4 md:w-3 text-white">
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
<div class="absolute inset-0 bg-gray-500/60"></div>
</button>
</div>
<div class="translate-y-0 opacity-100 visible px-3 pt-2 cursor-pointer pb-1 box-shadow-md bg-white rounded-xl relative w-[180px] sm:w-[200px] duration-1000 delay-75">
<div class="items-center space-x-1 sm:space-x-2 mb-1 sm:mb-1.5 hidden sm:flex">
<div class="overflow-hidden size-5 sm:size-6 shrink-0 rounded">
<img src="https://assets.geteasy.ai/workspaces/2353/bots/2770/public/0fd2dbe4-63bf-4d8e-871f-ab903f800fca.png" alt="Trợ lý AI của UMC" class="object-cover w-full h-full">
</div>
<div class="text-sm font-medium text-primary leading-[120%]">Trợ lý AI của UMC</div>
</div>
<div>
<div data-v-62f66be8="" class="vertical-custom-slide duration-500" style="height: 42px;">
<div data-v-62f66be8="" class="duration-1000 vertical-move" style="transform: translateY(-42px);">
<div data-v-62f66be8="" class="flex-1 text-sm text-gray-700 duration-1000 vertical-slide-item opacity-0">Xin chào, Em là trợ lý ảo của UMC 👋</div>
<div data-v-62f66be8="" class="flex-1 text-sm text-gray-700 duration-1000 vertical-slide-item opacity-100">Em rất sẵn lòng hỗ trợ Anh/Chị 😊</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!---->
<!---->
<div class="duration-300 relative w-fit border-[3px] rounded-lg delay-300 ml-auto border-transparent">
<!---->
<button aria-label="chat-button" class="flex relative justify-center items-center cursor-pointer chat-button disabled:pointer-events-none disabled:opacity-50 group rounded-lg bg-transparent shadow overflow-hidden">
<img src="https://assets.geteasy.ai/workspaces/2353/bots/2770/public/5026d034-feea-46db-a0c1-60c078ef5d5d.png" alt="Trợ lý AI của UMC" class="object-cover w-full h-full">
</button>
</div>
</div>
</div>
<!---->
<!---->
<!---->
<!---->
<!---->
</div>
</div>
<link rel="stylesheet" href="https://widget.geteasy.ai/dist/widget/main.css?v=1784950551115">
</template>
</div>
</div>
<th:block layout:fragment="scripts">
<!-- Swiper JS & Initialization -->
<script th:src="@{/js/swiper-bundle.min.js}"></script>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
@@ -679,10 +333,10 @@
return;
}
const sliderConfigs = [{
slug: 'gallery',
prev: '.btn-navigation-gallery-prev',
next: '.btn-navigation-gallery-next',
pagination: '.swiper-pagination-training-gallery'
slug: 'dao-tao-01',
prev: '.btn-navigation-dao-tao-01-prev',
next: '.btn-navigation-dao-tao-01-next',
pagination: ''
}, {
slug: 'khoa-hoc',
prev: '.btn-navigation-khoa-hoc-prev',
@@ -711,10 +365,17 @@
}];
sliderConfigs.forEach(function(config) {
const el = document.querySelector('.swiper-' + config.slug);
if (el && !el.classList.contains('swiper-initialized')) {
if (el) {
if (el.swiper) {
el.swiper.destroy(true, true);
}
new Swiper('.swiper-' + config.slug, {
slidesPerView: 'auto',
spaceBetween: 16,
observer: true,
observeParents: true,
watchOverflow: false,
rewind: true,
navigation: {
nextEl: config.next,
prevEl: config.prev,
@@ -734,6 +395,13 @@
}
window.addEventListener('load', initDaoTaoSliders);
</script>
</div>
<th:block layout:fragment="scripts">
<script>
if (typeof initDaoTaoSliders === 'function') {
initDaoTaoSliders();
}
</script>
</th:block>
</body>
</html>
@@ -156,10 +156,11 @@
</div>
</div>
<div th:utext="${doctorIntroHtml}"></div>
<div style="display: flex; flex-direction: row; justify-content: start; align-items: center; gap: var(--s2);" class="mb-6 lg:mt-6">
<button type="button" class="btn space-x-2 text-center label-3 w-[286px] !py-2 disabled:opacity-50 disabled:cursor-not-allowed text-white hover:opacity-90 transition-opacity" th:data-url="${doctor.bookingUrl != null and !doctor.bookingUrl.isEmpty() ? doctor.bookingUrl : '#'}" onclick="window.location.href=this.getAttribute('data-url')">
<div>Đặt lịch khám</div>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<a href="tel:18001115" style="color: white; text-decoration: none; width: 100%; display: flex; flex-direction: row; gap: 10px; justify-content: center; align-items: center;">Đặt lịch khám
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_83_297)">
<path d="M0 16.26C0 17.3 0.366667 18.18 1.1 18.9C1.83333 19.62 2.72 19.9867 3.76 20H16.26C17.2867 20 18.1667 19.6333 18.9 18.9C19.6333 18.1667 20 17.2867 20 16.26V3.76C20 2.94667 19.7667 2.22 19.3 1.58C18.8333 0.94 18.2333 0.493333 17.5 0.24V1.88C17.5 2.4 17.32 2.84667 16.96 3.22C16.6 3.59333 16.1533 3.77333 15.62 3.76C15.0867 3.74667 14.6467 3.56667 14.3 3.22C13.9533 2.87333 13.7733 2.42667 13.76 1.88V0H6.26V1.88C6.26 2.4 6.07333 2.84667 5.7 3.22C5.32667 3.59333 4.88667 3.77333 4.38 3.76C3.87333 3.74667 3.42667 3.56667 3.04 3.22C2.65333 2.87333 2.47333 2.42667 2.5 1.88V0.24C1.76667 0.506667 1.16667 0.953333 0.7 1.58C0.233333 2.20667 0 2.93333 0 3.76L0 16.26ZM2.5 16.26V6.26H17.5V16.26C17.5 16.6067 17.38 16.9 17.14 17.14C16.9 17.38 16.6067 17.5 16.26 17.5H3.76C3.41333 17.5 3.11333 17.38 2.86 17.14C2.60667 16.9 2.48667 16.6067 2.5 16.26ZM3.76 1.88C3.76 2.05333 3.82 2.2 3.94 2.32C4.06 2.44 4.20667 2.5 4.38 2.5C4.55333 2.5 4.7 2.44 4.82 2.32C4.94 2.2 5 2.05333 5 1.88V0H3.76V1.88ZM5 15H7.5V12.5H5V15ZM5 11.26H7.5V8.76H5V11.26ZM8.76 15H11.26V12.5H8.76V15ZM8.76 11.26H11.26V8.76H8.76V11.26ZM12.5 15H15V12.5H12.5V15ZM12.5 11.26H15V8.76H12.5V11.26ZM15 1.88C15 2.05333 15.06 2.2 15.18 2.32C15.3 2.44 15.4467 2.5 15.62 2.5C15.7933 2.5 15.94 2.44 16.06 2.32C16.18 2.2 16.2467 2.05333 16.26 1.88V0H15V1.88Z" fill="white"></path>
</g>
@@ -169,19 +170,14 @@
</clipPath>
</defs>
</svg>
</a>
</button>
<button type="button" class="btn space-x-2 text-center label-3 w-[286px] !py-2 disabled:opacity-50 disabled:cursor-not-allowed text-white hover:opacity-90 transition-opacity" th:data-url="${doctor.bookingUrl != null and !doctor.bookingUrl.isEmpty() ? doctor.bookingUrl : '#'}" onclick="window.location.href=this.getAttribute('data-url')">
<div>Đặt lịch khám</div>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_83_297)">
<path d="M0 16.26C0 17.3 0.366667 18.18 1.1 18.9C1.83333 19.62 2.72 19.9867 3.76 20H16.26C17.2867 20 18.1667 19.6333 18.9 18.9C19.6333 18.1667 20 17.2867 20 16.26V3.76C20 2.94667 19.7667 2.22 19.3 1.58C18.8333 0.94 18.2333 0.493333 17.5 0.24V1.88C17.5 2.4 17.32 2.84667 16.96 3.22C16.6 3.59333 16.1533 3.77333 15.62 3.76C15.0867 3.74667 14.6467 3.56667 14.3 3.22C13.9533 2.87333 13.7733 2.42667 13.76 1.88V0H6.26V1.88C6.26 2.4 6.07333 2.84667 5.7 3.22C5.32667 3.59333 4.88667 3.77333 4.38 3.76C3.87333 3.74667 3.42667 3.56667 3.04 3.22C2.65333 2.87333 2.47333 2.42667 2.5 1.88V0.24C1.76667 0.506667 1.16667 0.953333 0.7 1.58C0.233333 2.20667 0 2.93333 0 3.76L0 16.26ZM2.5 16.26V6.26H17.5V16.26C17.5 16.6067 17.38 16.9 17.14 17.14C16.9 17.38 16.6067 17.5 16.26 17.5H3.76C3.41333 17.5 3.11333 17.38 2.86 17.14C2.60667 16.9 2.48667 16.6067 2.5 16.26ZM3.76 1.88C3.76 2.05333 3.82 2.2 3.94 2.32C4.06 2.44 4.20667 2.5 4.38 2.5C4.55333 2.5 4.7 2.44 4.82 2.32C4.94 2.2 5 2.05333 5 1.88V0H3.76V1.88ZM5 15H7.5V12.5H5V15ZM5 11.26H7.5V8.76H5V11.26ZM8.76 15H11.26V12.5H8.76V15ZM8.76 11.26H11.26V8.76H8.76V11.26ZM12.5 15H15V12.5H12.5V15ZM12.5 11.26H15V8.76H12.5V11.26ZM15 1.88C15 2.05333 15.06 2.2 15.18 2.32C15.3 2.44 15.4467 2.5 15.62 2.5C15.7933 2.5 15.94 2.44 16.06 2.32C16.18 2.2 16.2467 2.05333 16.26 1.88V0H15V1.88Z" fill="white"></path>
</g>
<defs>
<clipPath id="clip0_83_297">
<rect width="20" height="20" fill="white"></rect>
</clipPath>
</defs>
</svg>
<a href="tel:18001115" style="color: white; text-decoration: none; width: 100%; display: flex; flex-direction: row; gap: 10px; justify-content: center; align-items: center;">Liên hệ
<svg class="text-white" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path></svg>
</a>
</button>
</div>
</div>
@@ -576,23 +572,24 @@
<div class="jam-accordion-content">
<div class="prose doctor-info-prose transition-all duration-200 !text-[14px] space-y-2 xl:m-3 m-2" th:utext="${doctor.workExperience}"></div>
</div>
<div class="jam-accordion active" th:if="${doctor.achievements != null and !doctor.achievements.isEmpty()}">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="bg-primary-600 xl:p-3 p-2 flex justify-between items-center cursor-pointer jam-accordion-title" tabindex="0" aria-expanded="true" aria-controls="association-content">
<div class="title-3 text-white">Hiệp hội chuyên môn &amp; Thành tựu</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down w-4 h-4 text-white duration-150 -rotate-180">
<path d="m6 9 6 6 6-6"></path>
</svg>
</div>
<div class="jam-accordion-content">
<div class="prose doctor-info-prose transition-all duration-200 !text-[14px] xl:m-3 m-2" th:utext="${doctor.achievements}"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="jam-accordion active" th:if="${doctor.achievements != null and !doctor.achievements.isEmpty()}">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="bg-primary-600 xl:p-3 p-2 flex justify-between items-center cursor-pointer jam-accordion-title" tabindex="0" aria-expanded="true" aria-controls="association-content">
<div class="title-3 text-white">Hiệp hội chuyên môn &amp; Thành tựu</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down w-4 h-4 text-white duration-150 -rotate-180">
<path d="m6 9 6 6 6-6"></path>
</svg>
</div>
<div class="jam-accordion-content">
<div class="prose doctor-info-prose transition-all duration-200 !text-[14px] xl:m-3 m-2" th:utext="${doctor.achievements}"></div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -602,5 +599,38 @@
<!--/$-->undefined
<!--$-->undefined
<!--/$-->undefined </main>undefined</div>undefined
<th:block layout:fragment="scripts">
<script th:inline="javascript">
/*<![CDATA[*/
document.addEventListener('DOMContentLoaded', function() {
var doctorCode = /*[[${doctor.doctorCode}]]*/ 'default';
if (doctorCode && doctorCode !== 'default') {
const container = document.getElementById('schedule-' + doctorCode);
if (container) {
fetch('/api/public/doctor-schedule/' + doctorCode)
.then(response => response.json())
.then(data => {
if (data && data.length > 0) {
let html = '<ul class="pl-0 mb-0 list-unstyled space-y-2">';
data.forEach(day => {
let dayText = day.dayOfWeek === 1 ? 'Chủ nhật' : 'Thứ ' + day.dayOfWeek;
let dateText = day.date ? ' <span class="text-gray-500">(' + day.date + ')</span>' : '';
html += '<li class="flex items-center gap-2"><i class="fas fa-check-circle text-green-500"></i> <strong>' + dayText + '</strong>' + dateText + '</li>';
});
html += '</ul>';
container.innerHTML = html;
} else {
container.innerHTML = '<span class="text-gray-500 italic">Bác sĩ chưa có lịch khám tuần này</span>';
}
})
.catch(err => {
container.innerHTML = '<span class="text-red-500">Lỗi tải dữ liệu lịch khám</span>';
});
}
}
});
/*]]>*/
</script>
</th:block>
</body>undefined
</html>
@@ -55,11 +55,11 @@
</div>
</div>
<div class="xl:px-6 px-4 xl:pb-6 pb-4 xl:mt-3 mt-2 flex items-center justify-between gap-x-2">
<a class="btn btn-outline-primary !inline-flex w-[149px] text-center !px-0 group items-center justify-center gap-1.5" th:href="@{/dat-lich}">
<a class="btn btn-outline-primary !inline-flex w-[149px] text-center !px-0 group items-center justify-center gap-1.5" style="text-decoration: none;" th:href="@{/bac-si/{code}(code=${doc.doctorCode})}">
<div class="text-white">Đặt lịch khám</div>
<svg class="text-white" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/><path d="M8 14h.01"/><path d="M12 14h.01"/><path d="M16 14h.01"/><path d="M8 18h.01"/><path d="M12 18h.01"/><path d="M16 18h.01"/></svg>
</a>
<a class="btn btn-outline-primary !inline-flex w-[149px] text-center !px-0 group items-center justify-center gap-1.5" href="tel:18001115">
<a class="btn btn-outline-primary !inline-flex w-[149px] text-center !px-0 group items-center justify-center gap-1.5" style="text-decoration: none;" href="tel:18001115">
<div class="text-white">Liên hệ</div>
<svg class="text-white" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
</a>
@@ -67,6 +67,35 @@
placeholder="e.g. BS. CKII">
</div>
<!-- Position -->
<div class="form-group">
<label for="doctorPosition" class="font-weight-bold">Position</label>
<input type="text" class="form-control" id="doctorPosition" th:field="*{position}"
placeholder="e.g. Trưởng khoa">
</div>
<!-- Gender -->
<div class="form-group">
<label for="doctorGender" class="font-weight-bold">Gender</label>
<select class="form-control" id="doctorGender" th:field="*{gender}">
<option value="">— Select Gender —</option>
<option value="nam">Nam</option>
<option value="nữ">Nữ</option>
</select>
</div>
<!-- Phone -->
<div class="form-group">
<label for="doctorPhone" class="font-weight-bold">Phone Number</label>
<input type="text" class="form-control" id="doctorPhone" th:field="*{phoneNumber}">
</div>
<!-- Email -->
<div class="form-group">
<label for="doctorEmail" class="font-weight-bold">Email</label>
<input type="email" class="form-control" id="doctorEmail" th:field="*{email}">
</div>
<!-- Specialty -->
<div class="form-group">
<label for="specialtyId" class="font-weight-bold">Specialty</label>
@@ -122,6 +151,14 @@
placeholder="https://booking.bvdaihoc.com.vn/...">
</div>
<!-- Active (Show on Web) -->
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="doctorActive" th:field="*{active}">
<label class="custom-control-label font-weight-bold" for="doctorActive">Show on Web (Active)</label>
</div>
</div>
<!-- Buttons -->
<div class="d-flex justify-content-between mt-4">
<a th:if="${!isNew}" th:href="@{/manage/doctors}" class="btn btn-secondary btn-sm">
@@ -156,11 +193,12 @@
<thead class="thead-light">
<tr>
<th width="5%">ID</th>
<th width="30%">Name</th>
<th width="15%">Title</th>
<th width="20%">Specialty</th>
<th width="10%">Active</th>
<th width="5%">Booking</th>
<th width="20%">Name</th>
<th width="15%">Title/Position</th>
<th width="10%">Gender</th>
<th width="15%">Contact</th>
<th width="15%">Specialty</th>
<th width="5%">Active</th>
<th width="15%">Actions</th>
</tr>
</thead>
@@ -171,7 +209,13 @@
<strong th:text="${doc.name}"></strong>
</td>
<td>
<span th:text="${doc.title}"></span>
<span th:text="${doc.title}"></span><br>
<small class="text-muted" th:text="${doc.position}"></small>
</td>
<td th:text="${doc.gender}"></td>
<td>
<small th:if="${doc.phoneNumber != null && !doc.phoneNumber.isEmpty()}" th:text="${doc.phoneNumber}"></small><br th:if="${doc.phoneNumber != null && !doc.phoneNumber.isEmpty()}">
<small th:if="${doc.email != null && !doc.email.isEmpty()}" th:text="${doc.email}"></small>
</td>
<td>
<span th:if="${doc.specialty != null}" th:text="${doc.specialty.name}" class="badge badge-info"></span>
@@ -181,10 +225,6 @@
<span th:if="${doc.active}" class="badge badge-success">Active</span>
<span th:if="${!doc.active}" class="badge badge-secondary">Inactive</span>
</td>
<td>
<a th:if="${doc.bookingUrl != null && !doc.bookingUrl.isEmpty()}" th:href="${doc.bookingUrl}" target="_blank" class="badge badge-success">Link</a>
<span th:if="${doc.bookingUrl == null || doc.bookingUrl.isEmpty()}" class="text-muted"></span>
</td>
<td class="text-center">
<a th:href="@{/manage/doctors/{id}/edit(id=${doc.id})}"
class="btn btn-sm btn-outline-info mr-1" title="Edit">
@@ -60,5 +60,7 @@
<script th:src="@{/js/ambient-video.js}"></script>
<!-- wp_footer hook -->
<th:block th:utext="${hookManager.doActionAndReturn('wp_footer')}"></th:block>
<!-- Page Scripts Fragment -->
<th:block layout:fragment="scripts"></th:block>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

+86
View File
@@ -0,0 +1,86 @@
import xml.etree.ElementTree as ET
import csv
import sys
import re
def clean_html(raw_html):
if not raw_html:
return ""
cleanr = re.compile('<.*?>')
cleantext = re.sub(cleanr, '', raw_html)
return cleantext.strip().replace('\n', ' ')
try:
tree = ET.parse('/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/bnhvinakhoaquctsiscnth.WordPress.2026-07-28 (5).xml')
root = tree.getroot()
except Exception as e:
print(f"Error parsing XML: {e}")
sys.exit(1)
ns = {
'wp': 'http://wordpress.org/export/1.2/',
'content': 'http://purl.org/rss/1.0/modules/content/'
}
# First pass to build a dictionary of attachment URLs
attachments = {}
for channel in root.findall('channel'):
for item in channel.findall('item'):
post_type_el = item.find('wp:post_type', ns)
if post_type_el is not None and post_type_el.text == 'attachment':
post_id_el = item.find('wp:post_id', ns)
attachment_url_el = item.find('wp:attachment_url', ns)
if post_id_el is not None and attachment_url_el is not None:
attachments[post_id_el.text] = attachment_url_el.text
csv_path = '/home/x79/sisvietnamvn_01/sisvietnamvn_main/doctors_from_xml.csv'
with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['Name', 'Position', 'Gender', 'Birthday', 'Phone', 'Email', 'Address', 'Order', 'ImageID', 'ImageURL', 'Content']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for channel in root.findall('channel'):
for item in channel.findall('item'):
post_type_el = item.find('wp:post_type', ns)
if post_type_el is not None and post_type_el.text == 'employee':
title = item.find('title')
name = title.text if title is not None else ""
content_el = item.find('content:encoded', ns)
content = clean_html(content_el.text) if content_el is not None else ""
doctor_data = {
'Name': name,
'Position': '',
'Gender': '',
'Birthday': '',
'Phone': '',
'Email': '',
'Address': '',
'Order': '',
'ImageID': '',
'ImageURL': '',
'Content': content[:200] # truncate for CSV view
}
for meta in item.findall('wp:postmeta', ns):
key_el = meta.find('wp:meta_key', ns)
val_el = meta.find('wp:meta_value', ns)
if key_el is not None and key_el.text and val_el is not None:
k = key_el.text
v = val_el.text or ""
if k == 'position': doctor_data['Position'] = v
elif k == 'gender': doctor_data['Gender'] = v
elif k == 'birthday': doctor_data['Birthday'] = v
elif k == 'phone_number': doctor_data['Phone'] = v
elif k == 'email': doctor_data['Email'] = v
elif k == 'address': doctor_data['Address'] = v
elif k == 'order': doctor_data['Order'] = v
elif k == 'image':
doctor_data['ImageID'] = v
doctor_data['ImageURL'] = attachments.get(v, '')
writer.writerow(doctor_data)
print(f"Successfully wrote CSV to {csv_path}")