diff --git a/app/forms/auth_forms.py b/app/forms/auth_forms.py
index b28b2ccfc633058096e24df08e07662887e99e1e..13b21f01f65977605f9e5e296f5cbc72db606de8 100644
--- a/app/forms/auth_forms.py
+++ b/app/forms/auth_forms.py
@@ -59,6 +59,14 @@ class ChangePasswordForm(FlaskForm):
     ])
     submit = SubmitField('Thay đổi mật khẩu')
 
+class ResetPasswordRequestForm(FlaskForm):
+    """Form yêu cầu đặt lại mật khẩu"""
+    email = StringField('Email', validators=[
+        DataRequired(message="Email không được để trống"),
+        Email(message="Vui lòng nhập địa chỉ email hợp lệ")
+    ])
+    submit = SubmitField('Gửi yêu cầu đặt lại mật khẩu')
+
 class NotificationSettingsForm(FlaskForm):
     """Form cài đặt thông báo"""
     email_notifications = BooleanField('Nhận thông báo qua email')
diff --git a/app/forms/dietitian_forms.py b/app/forms/dietitian_forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2fd31e77e232b2457afe06c27c6f2ad62da2204
--- /dev/null
+++ b/app/forms/dietitian_forms.py
@@ -0,0 +1,18 @@
+from flask_wtf import FlaskForm
+from wtforms import StringField, PasswordField, TextAreaField, SubmitField, ValidationError
+from wtforms.validators import DataRequired, Email, Length, EqualTo
+from app.models import User
+
+class DietitianForm(FlaskForm):
+    firstName = StringField('Họ', validators=[DataRequired(), Length(max=50)])
+    lastName = StringField('Tên', validators=[DataRequired(), Length(max=50)])
+    email = StringField('Email', validators=[DataRequired(), Email(), Length(max=100)])
+    password = PasswordField('Mật khẩu', validators=[DataRequired(), Length(min=6)])
+    phone = StringField('Số điện thoại', validators=[Length(max=20)])
+    specialization = StringField('Chuyên môn', validators=[Length(max=100)])
+    notes = TextAreaField('Ghi chú')
+    submit = SubmitField('Tạo tài khoản')
+
+    def validate_email(self, field):
+        if User.query.filter_by(email=field.data).first():
+            raise ValidationError('Email này đã được sử dụng.')
\ No newline at end of file
diff --git a/app/models/__init__.py b/app/models/__init__.py
index 7de59ffb51e23f4c8667ef2caab0e39524d43b2a..42207d7857f32f79a55f99f90eb17631ac8f0e5c 100644
--- a/app/models/__init__.py
+++ b/app/models/__init__.py
@@ -7,10 +7,10 @@ from .referral import Referral, ReferralStatus
 from .procedure import Procedure
 from .report import Report
 from .uploaded_file import UploadedFile
-from .dietitian import Dietitian, DietitianStatus
 from .activity_log import ActivityLog
 from .notification import Notification
-from .support import SupportMessage, SupportMessageReadStatus
+from .support import SupportMessage
+from .patient_dietitian_assignment import PatientDietitianAssignment
 
 # Import bất kỳ model bổ sung nào ở đây 
 
@@ -23,9 +23,8 @@ __all__ = [
     'Referral', 'ReferralStatus',
     'Report',
     'UploadedFile',
-    'Dietitian', 'DietitianStatus',
     'ActivityLog',
     'Notification',
-    'DietitianProfile',
-    'SupportMessage', 'SupportMessageReadStatus'
+    'SupportMessage',
+    'PatientDietitianAssignment',
 ] 
\ No newline at end of file
diff --git a/app/models/dietitian.py b/app/models/dietitian.py
index f5afef264ea823b6f5f6f9b646f41ba56e36da70..56fab49614f7731ecd2586771c895dd1ae1b2aff 100644
--- a/app/models/dietitian.py
+++ b/app/models/dietitian.py
@@ -7,7 +7,6 @@ import enum
 class DietitianStatus(enum.Enum):
     AVAILABLE = "AVAILABLE"
     UNAVAILABLE = "UNAVAILABLE"
-    ON_LEAVE = "ON_LEAVE"
 
 class Dietitian(db.Model):
     """Dietitian model containing dietitian information"""
@@ -47,8 +46,8 @@ class Dietitian(db.Model):
         return 0
     
     def update_status_based_on_patient_count(self):
-        """Cập nhật trạng thái dựa trên số lượng bệnh nhân."""
-        if self.patient_count >= 6:
+        """Cập nhật trạng thái chỉ dựa trên số lượng bệnh nhân."""
+        if self.patient_count >= 10:
             self.status = DietitianStatus.UNAVAILABLE
         else:
             self.status = DietitianStatus.AVAILABLE
diff --git a/app/models/encounter.py b/app/models/encounter.py
index 287df273bdbcc58d0498a49d079a16207d82b2bf..9e26d8e3d22a6cc7280b068ccf4a65d68d686c8a 100644
--- a/app/models/encounter.py
+++ b/app/models/encounter.py
@@ -32,6 +32,7 @@ class Encounter(db.Model):
     assigned_dietitian = relationship('User', foreign_keys=[dietitian_id], backref='assigned_encounters')
     measurements = relationship('PhysiologicalMeasurement', backref='encounter', lazy='dynamic', cascade='all, delete-orphan')
     referrals = relationship('Referral', back_populates='encounter', lazy='dynamic', cascade='all, delete-orphan')
+    procedures = relationship('Procedure', back_populates='encounter', lazy='dynamic', cascade='all, delete-orphan')
 
     def __repr__(self):
         display_id = self.custom_encounter_id if self.custom_encounter_id else self.encounterID
diff --git a/app/models/patient_dietitian_assignment.py b/app/models/patient_dietitian_assignment.py
new file mode 100644
index 0000000000000000000000000000000000000000..01f005ea3b00c613b984eca1de837b4463be1819
--- /dev/null
+++ b/app/models/patient_dietitian_assignment.py
@@ -0,0 +1,47 @@
+from app import db
+from datetime import datetime
+from sqlalchemy.dialects.mysql import INTEGER # Sử dụng INTEGER(unsigned=True) nếu cần
+
+class PatientDietitianAssignment(db.Model):
+    __tablename__ = 'patient_dietitian_assignments'
+
+    id = db.Column(INTEGER(unsigned=True), primary_key=True)
+    patient_id = db.Column(db.String(50), db.ForeignKey('patients.id', ondelete='CASCADE'), nullable=False, index=True)
+    dietitian_id = db.Column(db.Integer, db.ForeignKey('users.userID', ondelete='CASCADE'), nullable=False, index=True)
+    assignment_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, index=True)
+    end_date = db.Column(db.DateTime, nullable=True) # Ngày kết thúc assignment (nếu có)
+    is_active = db.Column(db.Boolean, default=True, nullable=False, index=True) # Trạng thái hiện tại của assignment
+    notes = db.Column(db.Text, nullable=True) # Ghi chú thêm (tùy chọn)
+
+    # Relationships (optional but helpful)
+    # patient = db.relationship('Patient', backref=db.backref('dietitian_assignments', lazy='dynamic')) # Có thể gây circular import nếu không cẩn thận
+    # dietitian = db.relationship('User', backref=db.backref('assigned_patients', lazy='dynamic')) # Tương tự
+
+    # Unique constraint to prevent duplicate active assignments (optional but recommended)
+    # Bỏ postgresql_where để tương thích với các dialect khác hoặc phiên bản cũ hơn
+    # Đổi tên constraint để tránh lỗi nếu constraint cũ đã tồn tại
+    __table_args__ = (db.UniqueConstraint('patient_id', 'dietitian_id', name='uq_patient_dietitian_assignment'),)
+
+
+    def __repr__(self):
+        status = 'Active' if self.is_active else 'Inactive'
+        return f'<PatientDietitianAssignment Patient:{self.patient_id} - Dietitian:{self.dietitian_id} ({status})>'
+
+    # Phương thức helper để deactive assignment cũ khi tạo assignment mới (cần gọi trong logic route)
+    @staticmethod
+    def deactivate_existing_assignments(patient_id, new_assignment_id=None):
+        assignments_to_deactivate = PatientDietitianAssignment.query.filter_by(
+            patient_id=patient_id,
+            is_active=True
+        )
+        if new_assignment_id:
+            # Không deactivate assignment mới vừa được tạo
+            assignments_to_deactivate = assignments_to_deactivate.filter(PatientDietitianAssignment.id != new_assignment_id)
+            
+        count = assignments_to_deactivate.update({'is_active': False, 'end_date': datetime.utcnow()})
+        # db.session.commit() # Commit nên được thực hiện ở route
+        return count > 0 # Trả về True nếu có assignment nào bị deactive
+
+    @staticmethod
+    def get_active_assignment(patient_id):
+        return PatientDietitianAssignment.query.filter_by(patient_id=patient_id, is_active=True).first() 
\ No newline at end of file
diff --git a/app/models/procedure.py b/app/models/procedure.py
index 7f5ebdab0abaeaaa878df50ee740f8a446069a28..e9550d1d2bb55e6da267648c9b7daf8f007a66f9 100644
--- a/app/models/procedure.py
+++ b/app/models/procedure.py
@@ -25,7 +25,7 @@ class Procedure(db.Model):
     updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
     
     # Relationships
-    encounter = relationship('Encounter', backref='procedures')
+    encounter = relationship('Encounter', back_populates='procedures')
     patient = relationship('Patient', backref='procedures')
 
     def __repr__(self):
diff --git a/app/models/report.py b/app/models/report.py
index 0f7acf6151b24fc339c0beda887673ccfbec6b8c..cb144983814af153fd99fb37addc3fe5d36d7046 100644
--- a/app/models/report.py
+++ b/app/models/report.py
@@ -1,7 +1,16 @@
 from datetime import datetime
 from app import db
 from sqlalchemy.orm import relationship
+from sqlalchemy import Enum as SQLAlchemyEnum # Import Enum từ sqlalchemy
 from app.models.procedure import Procedure # Import Procedure
+import enum # Import thư viện enum của Python
+
+# Định nghĩa Enum cho Report Status
+class ReportStatus(enum.Enum):
+    DRAFT = "Draft"
+    PENDING = "Pending"
+    COMPLETED = "Completed"
+    CANCELLED = "Cancelled"
 
 class Report(db.Model):
     """Model for dietitian reports and assessments"""
@@ -23,7 +32,7 @@ class Report(db.Model):
 
     report_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
     report_type = db.Column(db.String(50), nullable=False) # e.g., 'initial_assessment', 'follow_up'
-    status = db.Column(db.String(20), default='Draft') # e.g., 'Draft', 'Pending', 'Completed'
+    status = db.Column(SQLAlchemyEnum(ReportStatus), default=ReportStatus.DRAFT, nullable=False)
     completed_date = db.Column(db.DateTime, nullable=True)
     
     # Assessment Fields
diff --git a/app/models/support.py b/app/models/support.py
index 30af6313d2c5c6ff028b77bb4964eb4875f348fc..d9021acef6453f6b9228c9ed24c42adc41a41aa1 100644
--- a/app/models/support.py
+++ b/app/models/support.py
@@ -1,4 +1,4 @@
-from .. import db
+from app import db
 from datetime import datetime
 from sqlalchemy.orm import relationship
 from .user import User
@@ -8,25 +8,9 @@ class SupportMessage(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     sender_id = db.Column(db.Integer, db.ForeignKey('users.userID'), nullable=False)
     content = db.Column(db.Text, nullable=False)
-    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
+    timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
 
-    sender = relationship('User', backref='sent_support_messages')
-    read_statuses = relationship('SupportMessageReadStatus', back_populates='message', cascade='all, delete-orphan')
+    sender = relationship('User', back_populates='sent_support_messages')
 
     def __repr__(self):
-        return f'<SupportMessage {self.id} from User {self.sender_id}>'
-
-class SupportMessageReadStatus(db.Model):
-    __tablename__ = 'support_message_read_status'
-    id = db.Column(db.Integer, primary_key=True)
-    user_id = db.Column(db.Integer, db.ForeignKey('users.userID'), nullable=False)
-    message_id = db.Column(db.Integer, db.ForeignKey('support_messages.id'), nullable=False)
-    read_at = db.Column(db.DateTime, default=datetime.utcnow)
-
-    user = relationship('User')
-    message = relationship('SupportMessage')
-
-    __table_args__ = (db.UniqueConstraint('user_id', 'message_id', name='uq_user_message_read'),)
-
-    def __repr__(self):
-        return f'<SupportMessageReadStatus User {self.user_id} read Message {self.message_id}>' 
\ No newline at end of file
+        return f'<SupportMessage {self.id} from User {self.sender_id}>' 
\ No newline at end of file
diff --git a/app/models/user.py b/app/models/user.py
index 33ef868273d7b537f24fa96f6edc286292232c8d..ea3c20e5e745b62e4ff20377421e9831d4bf1bce 100644
--- a/app/models/user.py
+++ b/app/models/user.py
@@ -21,11 +21,13 @@ class User(UserMixin, db.Model):
     role = Column(Enum('Admin', 'Dietitian', name='user_roles_new'), default='Dietitian', nullable=False)
     created_at = Column(DateTime(timezone=True), server_default=func.now())
     last_login = Column(DateTime(timezone=True))
+    last_support_visit = Column(DateTime(timezone=True), nullable=True)
     
     # Relationships
     dietitian = relationship("Dietitian", back_populates="user", uselist=False, cascade="all, delete-orphan")
     activities = relationship("ActivityLog", back_populates="user", lazy='dynamic')
     assigned_patients = relationship('Patient', back_populates='assigned_dietitian', foreign_keys='[Patient.assigned_dietitian_user_id]')
+    sent_support_messages = relationship("SupportMessage", back_populates="sender")
 
     def set_password(self, password):
         self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
diff --git a/app/routes/auth.py b/app/routes/auth.py
index e3a41551c78b5ca91ec5fd56217cd7b72d208162..aceb2ea24fdb213247b1a7f4b0fef151f4b64c66 100644
--- a/app/routes/auth.py
+++ b/app/routes/auth.py
@@ -5,7 +5,7 @@ from datetime import datetime
 from app import db, bcrypt
 from app.models.user import User
 from app.models.dietitian import Dietitian, DietitianStatus
-from app.forms.auth_forms import LoginForm, EditProfileForm, ChangePasswordForm, NotificationSettingsForm
+from app.forms.auth_forms import LoginForm, EditProfileForm, ChangePasswordForm, NotificationSettingsForm, ResetPasswordRequestForm
 from ..utils.validators import is_safe_url, validate_password, validate_email, sanitize_input
 from sqlalchemy.sql import text
 from app.utils.decorators import admin_required
@@ -86,62 +86,88 @@ def logout():
 @auth_bp.route('/profile')
 @login_required
 def profile():
-    """Hiển thị trang hồ sơ người dùng"""
-    # Đếm số lượng tải lên bằng SQL trực tiếp chỉ với các cột chính
-    upload_count = db.session.execute(
-        text("SELECT COUNT(*) FROM uploadedfiles WHERE userID = :user_id"), 
-        {"user_id": current_user.userID}
-    ).scalar()
-    
-    return render_template(
-        'profile.html',
-        title='Hồ sơ người dùng',
-        upload_count=upload_count
-    )
+    """Hiển thị trang hồ sơ người dùng, tùy theo vai trò."""
+    if current_user.role == 'Admin':
+        # Logic cho admin (nếu cần thêm dữ liệu riêng)
+        return render_template('admin/profile.html', title='Admin Profile')
+    else:
+        # Logic cho các vai trò khác (ví dụ: Dietitian)
+        upload_count = db.session.execute(
+            text("SELECT COUNT(*) FROM uploadedfiles WHERE userID = :user_id"), 
+            {"user_id": current_user.userID}
+        ).scalar()
+        
+        return render_template(
+            'profile.html', 
+            title='Hồ sơ người dùng',
+            upload_count=upload_count
+        )
 
-@auth_bp.route('/profile/edit', methods=['GET', 'POST'])
+@auth_bp.route('/edit-profile', methods=['GET', 'POST'])
 @login_required
 def edit_profile():
-    """Xử lý chỉnh sửa hồ sơ người dùng"""
-    # Truyền current_user vào form để validate email gốc
-    form = EditProfileForm(obj=current_user)
-    
-    if form.validate_on_submit():
-        # Kiểm tra email trùng lặp (trừ email của chính người dùng hiện tại)
-        existing_user = User.query.filter(User.email == form.email.data, User.userID != current_user.userID).first()
-        if existing_user:
-            flash('Email này đã được sử dụng bởi người dùng khác.', 'danger')
-            # Render lại template với lỗi thay vì redirect
-            return render_template('edit_profile.html', title='Chỉnh sửa Hồ sơ', form=form)
+    user = current_user
+    # Xác định template dựa trên vai trò
+    if user.is_admin:
+        template = 'admin/edit_profile.html'
+    elif user.role == 'Dietitian':
+        template = 'edit_profile.html' # Template cho dietitian
+    else:
+        flash('Bạn không có quyền chỉnh sửa hồ sơ này.', 'warning')
+        return redirect(url_for('main.handle_root'))
 
-        # Cập nhật thông tin User
-        current_user.firstName = form.firstName.data
-        current_user.lastName = form.lastName.data
-        current_user.email = form.email.data
-        current_user.phone = form.phone.data # Cập nhật phone nếu có
+    # Lấy dietitian profile nếu là dietitian (sử dụng tên relationship đúng: dietitian)
+    dietitian_profile = user.dietitian if user.role == 'Dietitian' else None
 
-        # Nếu là Dietitian và có profile, cập nhật cả profile Dietitian
-        if current_user.role == 'Dietitian' and current_user.dietitian:
-            current_user.dietitian.firstName = form.firstName.data
-            current_user.dietitian.lastName = form.lastName.data
-            current_user.dietitian.email = form.email.data # Giữ email đồng bộ
-            current_user.dietitian.phone = form.phone.data
-            # Cập nhật các trường dietitian khác nếu có trong form
-            if hasattr(form, 'specialization'):
-                current_user.dietitian.specialization = form.specialization.data
-            if hasattr(form, 'notes'):
-                current_user.dietitian.notes = form.notes.data
+    # Khởi tạo form với dữ liệu hiện tại
+    if request.method == 'GET':
+        # Truyền dietitian_profile vào form nếu là Dietitian
+        form = EditProfileForm(obj=user, dietitian_profile=dietitian_profile)
+        # Thêm thuộc tính role_display nếu là Admin
+        if user.is_admin:
+             form.role_display = user.role # Để hiển thị trong template admin
+    else: # POST request
+        # Tạo instance form để validate dữ liệu gửi lên
+        form = EditProfileForm(request.form)
+        # Cần set lại dietitian_profile cho form instance khi POST để xử lý logic dietitian
+        form.dietitian_profile = dietitian_profile
+
+    if form.validate_on_submit():
+        # Cập nhật thông tin User chung
+        user.firstName = form.firstName.data
+        user.lastName = form.lastName.data
+        user.email = form.email.data
+        user.phone = form.phone.data
+
+        # Cập nhật thông tin Dietitian riêng nếu là Dietitian
+        if dietitian_profile and user.role == 'Dietitian':
+            if hasattr(form, 'specialization'): # Kiểm tra field tồn tại trong form
+                 dietitian_profile.specialization = form.specialization.data
+            if hasattr(form, 'notes'): # Kiểm tra field tồn tại trong form
+                 dietitian_profile.notes = form.notes.data
+            # Giữ thông tin user và dietitian profile đồng bộ
+            dietitian_profile.firstName = user.firstName
+            dietitian_profile.lastName = user.lastName
+            dietitian_profile.email = user.email
+            dietitian_profile.phone = user.phone
 
         try:
             db.session.commit()
-            flash('Hồ sơ của bạn đã được cập nhật thành công!', 'success')
-            return redirect(url_for('auth.profile')) # Redirect về trang profile sau khi thành công
+            flash('Thông tin hồ sơ đã được cập nhật thành công.', 'success')
+            return redirect(url_for('auth.profile'))
         except Exception as e:
             db.session.rollback()
-            flash(f'Lỗi khi cập nhật hồ sơ: {str(e)}', 'danger')
+            current_app.logger.error(f"Lỗi khi cập nhật hồ sơ user {user.userID}: {e}")
+            flash('Đã xảy ra lỗi khi cập nhật hồ sơ.', 'danger')
 
-    # Nếu là GET request hoặc form validation thất bại, render template
-    return render_template('edit_profile.html', title='Chỉnh sửa Hồ sơ', form=form)
+    # Nếu form không hợp lệ hoặc là GET request, hiển thị lại form
+    # Cần đảm bảo form.role_display được set lại cho GET request của Admin
+    if request.method == 'GET' and user.is_admin:
+        form.role_display = user.role
+        
+    password_form = ChangePasswordForm() # Luôn cần form đổi mật khẩu
+
+    return render_template(template, title='Chỉnh sửa Hồ sơ', form=form, password_form=password_form)
 
 @auth_bp.route('/change-password', methods=['POST'])
 @login_required
@@ -349,9 +375,10 @@ def reset_password_request():
     if current_user.is_authenticated:
         return redirect(url_for('dashboard.index'))
     
-    if request.method == 'POST':
-        email = request.form.get('email')
-        
+    form = ResetPasswordRequestForm()
+    
+    if form.validate_on_submit():
+        email = form.email.data
         user = User.query.filter_by(email=email).first()
         if user:
             # Gửi email đặt lại mật khẩu (sẽ triển khai sau)
@@ -362,4 +389,4 @@ def reset_password_request():
             
         return redirect(url_for('auth.login'))
         
-    return render_template('reset_password.html')
+    return render_template('auth/reset_password.html', form=form, title='Yêu cầu Đặt lại Mật khẩu')
diff --git a/app/routes/dashboard.py b/app/routes/dashboard.py
index d27ac42d6061bbca4849f00d9ce0989c4f763f0b..9d480f648c40241031df3e13441a8e2b9f8d12c4 100644
--- a/app/routes/dashboard.py
+++ b/app/routes/dashboard.py
@@ -7,12 +7,25 @@ from app.models.referral import Referral, ReferralStatus
 from app.models.procedure import Procedure
 from app.models.report import Report
 from app.models.measurement import PhysiologicalMeasurement
+from app.models.user import User
+from app.models.dietitian import Dietitian
 from sqlalchemy import func, case, or_, and_, desc
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
 import json
+from collections import defaultdict
 
 dashboard_bp = Blueprint('dashboard', __name__, url_prefix='/dashboard')
 
+def format_status_label(status_input):
+    """Helper function to format status strings or enum members for display."""
+    if hasattr(status_input, 'name'): # Check if it's an Enum member
+        status_str = status_input.name
+    elif isinstance(status_input, str):
+        status_str = status_input
+    else:
+        return "Unknown"
+    return status_str.replace('_', ' ').title()
+
 # Route chính của dashboard ('/dashboard/index')
 @dashboard_bp.route('/index')
 @login_required
@@ -21,114 +34,163 @@ def index():
     if current_user.role == 'Dietitian':
         return redirect(url_for('dietitian.dashboard'))
         
-    # Get statistics for dashboard
+    # Get statistics for dashboard cards
     stats = {
         'total_patients': Patient.query.count(),
-        'new_referrals': Referral.query.filter_by(referral_status=ReferralStatus.DIETITIAN_UNASSIGNED).count(),
+        'new_referrals': Referral.query.filter(Referral.referral_status == ReferralStatus.DIETITIAN_UNASSIGNED).count(),
         'procedures_today': Procedure.query.filter(
-            func.date(Procedure.procedureDateTime) == func.date(datetime.now())
+            func.date(Procedure.procedureDateTime) == func.current_date()
         ).count(),
         'pending_reports': Report.query.filter(Report.status == 'Pending').count()
     }
     
-    # Get recent patients for dashboard, sắp xếp theo ngày nhập viện mới nhất
+    # Get recent patients
     recent_patients = Patient.query.order_by(desc(Patient.admission_date)).limit(5).all()
     
-    # Get recent referrals
-    recent_referrals = (Referral.query
-                       .join(Encounter, Referral.encounter_id == Encounter.encounterID)
-                       .join(Patient, Encounter.patient_id == Patient.id)
-                       .add_columns(Patient.firstName, Patient.lastName, Patient.id)
-                       .order_by(Referral.created_at.desc())
-                       .limit(5)
-                       .all())
+    # Get recent referrals (Consider adding patient name/info)
+    recent_referrals = Referral.query.order_by(desc(Referral.created_at)).limit(5).all()
     
-    # Get BMI distribution
-    bmi_category = case(
+    # --- BMI Distribution --- 
+    bmi_category_case = case(
         (Patient.bmi < 18.5, 'Underweight'),
         (Patient.bmi.between(18.5, 24.9), 'Normal'),
-        (Patient.bmi.between(25.0, 29.9), 'Overweight'),
-        (Patient.bmi >= 30.0, 'Obese'),
+        (Patient.bmi.between(25, 29.9), 'Overweight'), # Corrected range
+        (Patient.bmi >= 30, 'Obese'),
         else_='Unknown'
     ).label('bmi_category')
     
-    bmi_stats = db.session.query(
-        bmi_category,
+    bmi_stats_query = db.session.query(
+        bmi_category_case,
         func.count().label('count')
     ).filter(Patient.bmi != None).group_by('bmi_category').all()
-    
-    # Get referrals over time for the past 90 days
-    end_date = datetime.now()
-    start_date = end_date - timedelta(days=90)
-    referral_dates = {}
-    
-    for i in range(90):
-        date = (end_date - timedelta(days=i)).strftime('%Y-%m-%d')
-        referral_dates[date] = 0
-    
-    referrals_by_date = db.session.query(
+    # Convert to list of lists for JS chart
+    bmi_stats = [[category, count] for category, count in bmi_stats_query if category != 'Unknown']
+    # ------------------------
+    
+    # --- Referrals Timeline (Bắt đầu từ 17/04 năm nay) --- 
+    end_date = datetime.utcnow().date() 
+    current_year = end_date.year
+    try:
+        start_date = datetime(current_year, 4, 17).date()
+    except ValueError: # Xử lý trường hợp ngày không hợp lệ (ví dụ: năm nhuận?)
+        # Nên log lỗi hoặc xử lý phù hợp, tạm thời dùng ngày đầu năm
+        start_date = datetime(current_year, 1, 1).date()
+        print(f"Warning: Could not create date April 17th, {current_year}. Using start of year.")
+
+    # Đảm bảo start_date không ở tương lai
+    if start_date > end_date:
+        start_date = end_date 
+
+    # Use func.date for database-level date extraction
+    referrals_by_date_query = db.session.query(
         func.date(Referral.referralRequestedDateTime).label('date'),
         func.count().label('count')
     ).filter(
-        Referral.referralRequestedDateTime.between(start_date, end_date)
-    ).group_by('date').all()
+        func.date(Referral.referralRequestedDateTime).between(start_date, end_date)
+    ).group_by(func.date(Referral.referralRequestedDateTime)).all()
     
-    for date, count in referrals_by_date:
-        referral_dates[date.strftime('%Y-%m-%d')] = count
+    # Prepare the timeline data, ensuring all dates in the range are present
+    delta_days = (end_date - start_date).days
+    # Tạo dict với đầy đủ các ngày từ start_date đến end_date
+    referral_timeline_dict = { (start_date + timedelta(days=i)).isoformat(): 0 for i in range(delta_days + 1) }
+    for date_obj, count in referrals_by_date_query:
+        if date_obj and date_obj.isoformat() in referral_timeline_dict:
+             referral_timeline_dict[date_obj.isoformat()] = count
     
     referral_timeline = [
+        {'date': date_str, 'count': count} 
+        for date_str, count in sorted(referral_timeline_dict.items())
+    ]
+    # -------------------------------------------
+
+    # --- Dietitian Workload --- 
+    dietitian_workload_query = (db.session.query(
+        User.firstName, 
+        User.lastName,
+        func.count(Patient.id).label('patient_count')
+    )
+    .select_from(User)
+    .outerjoin(Patient, User.userID == Patient.assigned_dietitian_user_id)
+    .filter(User.role == 'Dietitian') # Ensure we only query Dietitians
+    .group_by(User.userID, User.firstName, User.lastName) # Group by user details
+    .order_by(desc('patient_count'))
+    .all())
+
+    # Get total count of patients who have an assigned dietitian
+    assigned_patients_count = db.session.query(func.count(Patient.id)).filter(Patient.assigned_dietitian_user_id != None).scalar()
+    total_patients_count = stats['total_patients']
+    unassigned_patients_count = total_patients_count - (assigned_patients_count or 0)
+
+    dietitian_workload_chart_data = [
         {
-            'date': date,
-            'count': count
-        } for date, count in sorted(referral_dates.items())
+            "name": f"{d.firstName} {d.lastName}" if d.firstName else "Unknown Dietitian", 
+            "count": d.patient_count
+        } for d in dietitian_workload_query
     ]
-    
-    # Check for nutritional alerts
+    if unassigned_patients_count > 0:
+        dietitian_workload_chart_data.append({"name": "Unassigned", "count": unassigned_patients_count})
+    # ---------------------------
+
+    # --- Patient Status Distribution --- 
+    patient_status_stats_query = db.session.query(
+        Patient.status,
+        func.count().label('count')
+    ).group_by(Patient.status).all()
+    patient_status_stats = {format_status_label(status): count for status, count in patient_status_stats_query}
+    # -----------------------------------
+
+    # --- Report Status Distribution (Using String) ---
+    report_status_stats_query = db.session.query(
+        Report.status, # String column
+        func.count().label('count')
+    ).group_by(Report.status).all()
+    # Use the string status directly as key, apply formatting if needed
+    report_status_stats = {status.value: count for status, count in report_status_stats_query if status}
+    # ---------------------------------------------------
+
+    # --- Referral Status Distribution --- 
+    referral_status_stats_query = db.session.query(
+        Referral.referral_status, 
+        func.count().label('count')
+    ).group_by(Referral.referral_status).all()
+    referral_status_stats = {format_status_label(status): count for status, count in referral_status_stats_query}
+    # -------------------------------------
+
+    # --- Alerts --- (Keeping existing logic)
     alerts = []
-    
-    # Alert for patients with critical BMI
-    critical_bmi_patients = (Patient.query
-                            .filter((Patient.bmi < 16) | (Patient.bmi > 40))
-                            .all())
-    
-    if critical_bmi_patients:
+    critical_bmi_patients = Patient.query.filter(or_(Patient.bmi < 16, Patient.bmi > 40)).count()
+    if critical_bmi_patients > 0:
         alerts.append({
             'type': 'danger',
-            'message': f'{len(critical_bmi_patients)} patients with critical nutritional status detected',
-            'link': '/patients?critical=true'
+            'message': f'{critical_bmi_patients} patients with critical nutritional status detected',
+            'link': url_for('patients.index', bmi_filter='critical') # Example link
         })
     
-    # Alert for overdue referrals
     overdue_referrals = Referral.query.filter(
         Referral.referral_status.in_([ReferralStatus.DIETITIAN_UNASSIGNED, ReferralStatus.WAITING_FOR_REPORT]),
-        Referral.referralRequestedDateTime < (datetime.now() - timedelta(days=2))
+        Referral.referralRequestedDateTime < (datetime.utcnow() - timedelta(days=2))
     ).count()
-    
-    if overdue_referrals:
+    if overdue_referrals > 0:
         alerts.append({
             'type': 'warning',
             'message': f'{overdue_referrals} referrals pending for more than 48 hours',
-            'link': '/dashboard/referrals?overdue=true'
-        })
-    
-    # Alert for monthly report
-    today = datetime.now()
-    if today.day > 25:  # Near end of month
-        alerts.append({
-            'type': 'info',
-            'message': 'Monthly report is ready for export',
-            'link': '/reports/generate?type=monthly'
+            'link': url_for('referral.index', status='overdue') # Placeholder link
         })
+    # --------------
     
     return render_template(
         'dashboard.html',
         stats=stats,
         recent_patients=recent_patients,
-        recent_referrals=recent_referrals,
-        bmi_stats=bmi_stats,
+        recent_referrals=recent_referrals, # Pass recent referrals
+        bmi_stats=bmi_stats, # Pass as list of lists
         referral_timeline=referral_timeline,
+        dietitian_workload=dietitian_workload_chart_data,
+        patient_status_stats=patient_status_stats, # Pass formatted dict
+        report_status_stats=report_status_stats,   # Pass dict with string keys
+        referral_status_stats=referral_status_stats, # Pass formatted dict
         alerts=alerts,
-        PatientStatus=PatientStatus
+        PatientStatus=PatientStatus # Pass Enum for template logic if needed
     )
 
 @dashboard_bp.route('/api/stats')
diff --git a/app/routes/dietitian.py b/app/routes/dietitian.py
index a38bb6a781d44fde3e5c3350b74acc76ddaf2ff6..5038d64fdfd2e686df2f27fdc05a59d01c5d3c66 100644
--- a/app/routes/dietitian.py
+++ b/app/routes/dietitian.py
@@ -13,6 +13,9 @@ from app.forms.procedure import ProcedureForm # Import ProcedureForm
 from sqlalchemy import func, case, desc
 from datetime import datetime, timedelta
 from flask_wtf import FlaskForm # Đảm bảo import ở đầu file
+from app.models import Patient, User, Procedure, Encounter, PatientDietitianAssignment # Giữ lại import cần thiết
+from app.forms import ProcedureForm
+from app.utils.decorators import permission_required
 
 dietitian_bp = Blueprint('dietitian', __name__, url_prefix='/dietitian')
 
@@ -135,98 +138,150 @@ def dashboard():
 
 # --- Procedure Routes for Dietitians ---
 
-@dietitian_bp.route('/procedures')
-@dietitian_required
-def list_procedures():
+# --- THÊM LẠI ROUTE CHUNG CHO DANH SÁCH PROCEDURES CỦA DIETITIAN ---
+@dietitian_bp.route('/procedures', methods=['GET'])
+@login_required
+@permission_required('DIETITIAN', 'ADMIN') # Admin cũng có thể xem trang này
+def list_my_procedures(): # Đổi tên hàm để tránh xung đột
     """Hiển thị danh sách các procedures, có thể lọc theo bệnh nhân."""
     page = request.args.get('page', 1, type=int)
     per_page = 15 # Số lượng procedure mỗi trang
-    patient_filter_id = request.args.get('patient_id') # Giữ lại để lọc procedure
+    patient_filter_id = request.args.get('patient_id') # Lấy patient_id từ query string để lọc
 
     # --- Query chính lấy procedures --- 
+    # Bắt đầu với Procedure, join Patient và Encounter
     query = Procedure.query.join(Patient, Procedure.patient_id == Patient.id)\
-                         .join(Encounter, Procedure.encounter_id == Encounter.encounterID)\
+                         .outerjoin(Encounter, Procedure.encounter_id == Encounter.encounterID)\
                          .options(db.joinedload(Procedure.patient), db.joinedload(Procedure.encounter))
     
     # Nếu người dùng là Dietitian, chỉ hiển thị procedures của bệnh nhân họ quản lý
-    if not current_user.is_admin:
+    if current_user.role.upper() == 'DIETITIAN':
+        # Lọc dựa trên dietitian_id được gán cho BỆNH NHÂN
         query = query.filter(Patient.assigned_dietitian_user_id == current_user.userID)
+    # Admin có thể xem tất cả procedures
 
-    # Lọc theo bệnh nhân được chọn từ dropdown
+    # Lọc theo bệnh nhân được chọn từ dropdown (nếu có)
     if patient_filter_id:
         query = query.filter(Procedure.patient_id == patient_filter_id)
 
+    # Sắp xếp theo ngày giờ procedure giảm dần
     query = query.order_by(Procedure.procedureDateTime.desc())
     
+    # Thực hiện phân trang
     pagination = query.paginate(page=page, per_page=per_page, error_out=False)
     procedures = pagination.items
 
     # --- Lấy danh sách bệnh nhân cho dropdown filter --- 
     patients_query = Patient.query
-    # Nếu là Dietitian, chỉ lấy bệnh nhân được gán
-    if not current_user.is_admin:
+    # Nếu là Dietitian, chỉ lấy bệnh nhân được gán cho họ
+    if current_user.role.upper() == 'DIETITIAN':
         patients_query = patients_query.filter(Patient.assigned_dietitian_user_id == current_user.userID)
         
     patients_for_filter = patients_query.order_by(Patient.lastName, Patient.firstName).all()
 
-    # Tạo instance của EmptyForm
+    # Tạo instance của EmptyForm (cần thiết nếu template dùng modal delete)
     empty_form = EmptyForm()
 
+    # Render template dietitian_procedures.html
     return render_template('dietitian_procedures.html', 
                            procedures=procedures, 
                            pagination=pagination, 
                            patients_for_filter=patients_for_filter,
                            selected_patient_id=patient_filter_id,
                            empty_form=empty_form) # Truyền empty_form vào context
+# --- KẾT THÚC ROUTE CHUNG ---
 
-@dietitian_bp.route('/procedure/new/for_patient/<string:patient_id>', methods=['GET', 'POST'])
-@dietitian_required
+
+# Route mới để hiển thị danh sách Thủ thuật cho một bệnh nhân CỤ THỂ
+@dietitian_bp.route('/patient/<string:patient_id>/procedures', methods=['GET'])
+@login_required
+@permission_required('DIETITIAN', 'ADMIN') # Cho phép cả Admin xem
+def list_procedures(patient_id): # Tên hàm này giữ nguyên
+    patient = Patient.query.get_or_404(patient_id)
+    # Kiểm tra quyền truy cập của Dietitian
+    if current_user.role.upper() == 'DIETITIAN':
+        # Kiểm tra xem dietitian hiện tại có được gán cho bệnh nhân này không
+        if patient.assigned_dietitian_user_id != current_user.userID:
+            flash('You are not authorized to view this patient\'s procedures.', 'danger')
+            return redirect(url_for('dietitian.dashboard'))
+    # Admin có thể xem tất cả
+
+    procedures = Procedure.query.filter_by(patient_id=patient.id).order_by(Procedure.procedureDateTime.desc()).all()
+
+    # Kiểm tra xem có encounter nào đang ON_GOING không để bật/tắt nút Add
+    ongoing_encounter = Encounter.query.filter_by(
+        patient_id=patient.id,
+        status=EncounterStatus.ON_GOING
+    ).first()
+
+    # Render template list_patient_procedures.html thay vì list_procedures.html
+    return render_template('list_patient_procedures.html',
+                           patient=patient,
+                           procedures=procedures,
+                           has_ongoing_encounter=bool(ongoing_encounter))
+
+@dietitian_bp.route('/patient/<string:patient_id>/procedure/new', methods=['GET', 'POST'])
+@login_required
+@permission_required('DIETITIAN', 'ADMIN') # Cho phép cả Admin
 def new_procedure(patient_id):
-    """Hiển thị form và xử lý tạo Procedure mới cho bệnh nhân cụ thể."""
     patient = Patient.query.get_or_404(patient_id)
-    
-    # Lấy encounter_id từ URL nếu có (để chọn sẵn và khóa)
+    # Kiểm tra quyền truy cập của Dietitian
+    if current_user.role.upper() == 'DIETITIAN':
+        # Kiểm tra xem dietitian hiện tại có được gán cho bệnh nhân này không
+        if patient.assigned_dietitian_user_id != current_user.userID:
+            flash('You are not authorized to add procedures for this patient.', 'danger')
+            return redirect(url_for('.list_procedures', patient_id=patient_id))
+    # Admin có thể thêm
+
+    # Kiểm tra encounter đang diễn ra TRƯỚC khi tạo form
+    ongoing_encounter = Encounter.query.filter_by(
+        patient_id=patient.id,
+        status=EncounterStatus.ON_GOING
+    ).first()
+
+    if not ongoing_encounter:
+        flash('Cannot add a new procedure as there is no ongoing encounter for this patient.', 'warning')
+        # Kiểm tra xem có phải đang đến từ trang procedures chung không
+        referrer = request.referrer or ''
+        if 'procedures' in referrer and 'patient' not in referrer:
+            # Nếu đến từ trang procedures chung, redirect về đó với filter
+            return redirect(url_for('.list_my_procedures', patient_id=patient.id))
+        else:
+            # Ngược lại về trang procedures của bệnh nhân cụ thể
+            return redirect(url_for('.list_procedures', patient_id=patient_id))
+
+    # Sử dụng preselected_encounter_id để khóa encounter nếu đến từ report
     preselected_encounter_id = request.args.get('encounter_id', type=int)
     redirect_to_report_id = request.args.get('redirect_to_report', type=int)
-
-    # Kiểm tra xem có encounter nào đang ON_GOING không
-    on_going_encounters = Encounter.query.filter(
-        Encounter.patient_id == patient.id,
-        Encounter.status == EncounterStatus.ON_GOING
-    ).order_by(desc(Encounter.start_time)).all()
-
-    # Nếu không có encounter_id được truyền và không có encounter ON_GOING nào
-    if not preselected_encounter_id and not on_going_encounters:
-        flash("Encounter is either not started or completed, please check again!", 'warning')
-        # Chuyển hướng về trang danh sách procedure của bệnh nhân đó
-        return redirect(url_for('.list_procedures', patient_id=patient.id))
-    
-    # Khởi tạo form, truyền patient_id và encounter_id (nếu có)
-    # Truyền encounter_id vào để form biết cần khóa và chọn sẵn
     form = ProcedureForm(patient_id=patient.id, preselected_encounter_id=preselected_encounter_id)
-    
-    # Nếu không có preselected_encounter_id, chỉ cho phép chọn từ các encounter ON_GOING
+
+    # Logic xử lý choices của form.encounter_id giữ nguyên như cũ
     if not preselected_encounter_id:
+        # Lấy các encounter ON_GOING cho dropdown nếu không có preselected_id
+        on_going_encounters = Encounter.query.filter(
+            Encounter.patient_id == patient.id,
+            Encounter.status == EncounterStatus.ON_GOING
+        ).order_by(desc(Encounter.start_time)).all()
         form.encounter_id.choices = [
-            (enc.encounterID, f"{enc.custom_encounter_id or enc.encounterID} ({enc.start_time.strftime('%d/%m/%y %H:%M')})") 
+            (enc.encounterID, f"{enc.custom_encounter_id or enc.encounterID} ({enc.start_time.strftime('%d/%m/%y %H:%M')})")
             for enc in on_going_encounters
         ]
         form.encounter_id.choices.insert(0, ('', '-- Select Encounter --'))
-    # Nếu có preselected_encounter_id, đảm bảo nó nằm trong danh sách choices (dù bị disable)
     else:
-        # Lấy tất cả encounters để đảm bảo preselected_id có trong list
+        # Lấy tất cả encounters nếu có preselected_id để đảm bảo nó có trong list
         all_encounters = Encounter.query.filter_by(patient_id=patient.id).order_by(desc(Encounter.start_time)).all()
         form.encounter_id.choices = [
-            (enc.encounterID, f"{enc.custom_encounter_id or enc.encounterID} ({enc.start_time.strftime('%d/%m/%y %H:%M')})") 
+            (enc.encounterID, f"{enc.custom_encounter_id or enc.encounterID} ({enc.start_time.strftime('%d/%m/%y %H:%M')})")
             for enc in all_encounters
         ]
         # Không cần insert lựa chọn rỗng vì nó sẽ bị disable và chọn sẵn
 
     if form.validate_on_submit():
         try:
+            # Logic tạo Procedure giữ nguyên
             new_proc = Procedure(
                 patient_id=patient.id,
-                encounter_id=form.encounter_id.data, # Lấy từ form (sẽ là giá trị đã chọn sẵn nếu bị disable)
+                encounter_id=form.encounter_id.data,
                 procedureType=form.procedureType.data,
                 procedureName=form.procedureName.data,
                 procedureDateTime=form.procedureDateTime.data,
@@ -235,85 +290,127 @@ def new_procedure(patient_id):
                 procedureResults=form.procedureResults.data
             )
             db.session.add(new_proc)
-            db.session.flush() # Lấy ID của procedure mới
+            db.session.flush()
             new_procedure_id = new_proc.id
             db.session.commit()
             flash(f'Procedure "{new_proc.procedureType}" created successfully for patient {patient.full_name}.', 'success')
-            
-            # Kiểm tra xem có cần redirect về trang report không
+
+            # Redirect về trang REPORT nếu có redirect_to_report_id
             if redirect_to_report_id:
-                return redirect(url_for('report.edit_report', report_id=redirect_to_report_id, new_procedure_id=new_procedure_id))
+                return redirect(url_for('reports.view_report', report_id=redirect_to_report_id))
+            
+            # Kiểm tra xem có phải đang đến từ trang procedures chung không
+            referrer = request.referrer or ''
+            if 'procedures' in referrer and 'patient' not in referrer:
+                # Nếu đến từ trang procedures chung, redirect về đó với filter
+                return redirect(url_for('.list_my_procedures', patient_id=patient.id))
             else:
-                # Redirect về danh sách procedure, lọc theo bệnh nhân vừa tạo
+                # Ngược lại về trang procedures của bệnh nhân cụ thể
                 return redirect(url_for('.list_procedures', patient_id=patient.id))
+
         except Exception as e:
             db.session.rollback()
-            flash(f'Error creating procedure: {str(e)}', 'danger')
+            flash(f'Error creating procedure: {e}', 'danger')
 
-    # Render form nếu là GET request hoặc validation thất bại
-    return render_template('procedure_form.html', form=form, patient=patient, edit_mode=False, preselected_encounter_id=preselected_encounter_id)
+    # Render procedure_form.html giữ nguyên
+    return render_template('procedure_form.html', title='Add New Procedure', form=form, patient=patient, action="Add")
 
 @dietitian_bp.route('/procedure/<int:procedure_id>/edit', methods=['GET', 'POST'])
-@dietitian_required
+@login_required
+@permission_required('DIETITIAN', 'ADMIN')
 def edit_procedure(procedure_id):
-    """Hiển thị form và xử lý cập nhật Procedure."""
-    proc = Procedure.query.options(db.joinedload(Procedure.patient)).get_or_404(procedure_id)
-    patient = proc.patient
-    
-    # Permission check (ví dụ: chỉ dietitian được gán cho patient mới được sửa)
-    if not current_user.is_admin and patient.assigned_dietitian_user_id != current_user.userID:
-        flash('You do not have permission to edit this procedure.', 'danger')
-        return redirect(url_for('.list_procedures', patient_id=patient.id))
+    procedure = Procedure.query.get_or_404(procedure_id)
+    patient = Patient.query.get_or_404(procedure.patient_id)
+    # Kiểm tra quyền truy cập của Dietitian
+    if current_user.role.upper() == 'DIETITIAN':
+        # Kiểm tra xem dietitian hiện tại có được gán cho bệnh nhân này không
+        if patient.assigned_dietitian_user_id != current_user.userID:
+            flash('You are not authorized to edit procedures for this patient.', 'danger')
+            return redirect(url_for('.list_procedures', patient_id=patient.id))
+    # Admin có thể sửa
+
+    # Sử dụng preselected_encounter_id để khóa encounter
+    form = ProcedureForm(obj=procedure, patient_id=patient.id, preselected_encounter_id=procedure.encounter_id)
 
-    # Khởi tạo form với dữ liệu hiện có (obj=proc)
-    # Truyền patient_id để load encounter choices, preselect encounter hiện tại
-    form = ProcedureForm(obj=proc, patient_id=patient.id, preselected_encounter_id=proc.encounter_id)
+    # Logic xử lý choices của form.encounter_id giữ nguyên như cũ
+    all_encounters = Encounter.query.filter_by(patient_id=patient.id).order_by(desc(Encounter.start_time)).all()
+    form.encounter_id.choices = [
+            (enc.encounterID, f"{enc.custom_encounter_id or enc.encounterID} ({enc.start_time.strftime('%d/%m/%y %H:%M')})")
+            for enc in all_encounters
+    ]
 
     if form.validate_on_submit():
         try:
-            proc.procedureType = form.procedureType.data
-            proc.procedureName = form.procedureName.data
-            proc.procedureDateTime = form.procedureDateTime.data
-            proc.procedureEndDateTime = form.procedureEndDateTime.data
-            proc.description = form.description.data
-            proc.procedureResults = form.procedureResults.data
-            # Không cho sửa encounter_id khi edit?
-            # proc.encounter_id = form.encounter_id.data 
-            proc.updated_at = datetime.utcnow() # Giả sử có cột updated_at
+            # Cập nhật procedure
+            procedure.procedureType = form.procedureType.data
+            procedure.procedureName = form.procedureName.data
+            procedure.procedureDateTime = form.procedureDateTime.data
+            procedure.procedureEndDateTime = form.procedureEndDateTime.data
+            procedure.description = form.description.data
+            procedure.procedureResults = form.procedureResults.data
+            # Không cho sửa encounter_id sau khi tạo
 
             db.session.commit()
-            flash(f'Procedure "{proc.procedureType}" updated successfully.', 'success')
-            # Redirect về danh sách procedure, lọc theo bệnh nhân
+            flash(f'Procedure "{procedure.procedureType}" updated successfully.', 'success')
+            # Redirect về danh sách procedure của bệnh nhân
             return redirect(url_for('.list_procedures', patient_id=patient.id))
         except Exception as e:
             db.session.rollback()
-            flash(f'Error updating procedure: {str(e)}', 'danger')
-            
-    # Hiển thị form với dữ liệu hiện tại nếu là GET hoặc validation lỗi
-    return render_template('procedure_form.html', form=form, patient=patient, edit_mode=True, procedure=proc, preselected_encounter_id=proc.encounter_id)
+            flash(f'Error updating procedure: {e}', 'danger')
+
+    return render_template('procedure_form.html', title='Edit Procedure', form=form, patient=patient, action="Edit")
 
 @dietitian_bp.route('/procedure/<int:procedure_id>/delete', methods=['POST'])
-@dietitian_required
+@login_required
+@permission_required('DIETITIAN', 'ADMIN')
 def delete_procedure(procedure_id):
-    """Xử lý yêu cầu xóa Procedure."""
-    proc = Procedure.query.options(db.joinedload(Procedure.patient)).get_or_404(procedure_id)
-    patient_id_redirect = proc.patient_id # Lưu lại patient_id để redirect
-
-    # Permission check
-    if not current_user.is_admin and proc.patient.assigned_dietitian_user_id != current_user.userID:
-        flash('You do not have permission to delete this procedure.', 'danger')
-        return redirect(url_for('.list_procedures', patient_id=patient_id_redirect))
+    procedure = Procedure.query.get_or_404(procedure_id)
+    patient_id = procedure.patient_id # Lưu lại patient_id để redirect
+    patient = Patient.query.get_or_404(patient_id)
+    # Kiểm tra quyền truy cập của Dietitian
+    if current_user.role.upper() == 'DIETITIAN':
+        # Kiểm tra xem dietitian hiện tại có được gán cho bệnh nhân này không
+        if patient.assigned_dietitian_user_id != current_user.userID:
+            flash('You are not authorized to delete procedures for this patient.', 'danger')
+            return redirect(url_for('.list_procedures', patient_id=patient_id))
+    # Admin có thể xóa
 
     try:
-        procedure_info = f'{proc.procedureType} (ID: {proc.id})'
-        db.session.delete(proc)
+        db.session.delete(procedure)
         db.session.commit()
-        flash(f'Procedure {procedure_info} deleted successfully.', 'success')
+        flash(f'Procedure "{procedure.procedureType}" deleted successfully.', 'success')
     except Exception as e:
         db.session.rollback()
-        flash(f'Error deleting procedure: {str(e)}', 'danger')
+        flash(f'Error deleting procedure: {e}', 'danger')
+
+    # Redirect về danh sách procedure của bệnh nhân
+    return redirect(url_for('.list_procedures', patient_id=patient_id))
+
+# Route để hiển thị danh sách Thủ thuật cho một bệnh nhân cụ thể
+@dietitian_bp.route('/patient/<string:patient_id>/procedures', methods=['GET'])
+@login_required
+@permission_required('DIETITIAN')
+def list_patient_procedures(patient_id):
+    """Hiển thị danh sách các procedures của một bệnh nhân cụ thể."""
+    patient = Patient.query.get_or_404(patient_id)
+    # Kiểm tra quyền truy cập của dietitian (chỉ bệnh nhân được gán)
+    if not current_user.is_admin and patient.assigned_dietitian_user_id != current_user.userID:
+        abort(403)
+
+    # Lấy danh sách procedures của bệnh nhân này
+    procedures = Procedure.query.filter_by(patient_id=patient_id)\
+                            .options(db.joinedload(Procedure.encounter))\
+                            .order_by(Procedure.procedureDateTime.desc()).all()
+                            
+    # Kiểm tra xem có encounter nào đang ON_GOING để bật/tắt nút Add
+    has_ongoing_encounter = Encounter.query.filter(
+        Encounter.patient_id == patient.id,
+        Encounter.status == EncounterStatus.ON_GOING
+    ).first() is not None
 
-    # Redirect về danh sách procedure của bệnh nhân đó
-    return redirect(url_for('.list_procedures', patient_id=patient_id_redirect))
+    return render_template('list_patient_procedures.html', 
+                           patient=patient, 
+                           procedures=procedures,
+                           has_ongoing_encounter=has_ongoing_encounter)
 
 # (TODO later: Routes for viewing procedures if needed separately) 
\ No newline at end of file
diff --git a/app/routes/dietitians.py b/app/routes/dietitians.py
index 1ed3f0814b7338c87814cd01e1e25faca5a55eb6..f986117d27ae6bffd97fb27b9f54506da6479190 100644
--- a/app/routes/dietitians.py
+++ b/app/routes/dietitians.py
@@ -7,6 +7,8 @@ from app.models.encounter import Encounter
 from sqlalchemy import desc, or_, func, text
 from datetime import datetime
 from app.models.user import User
+from flask import current_app
+from app.forms.dietitian_forms import DietitianForm
 
 dietitians_bp = Blueprint('dietitians', __name__, url_prefix='/dietitians')
 
@@ -96,43 +98,48 @@ def show(id):
 @dietitians_bp.route('/new', methods=['GET', 'POST'])
 @login_required
 def new():
-    """Tạo chuyên gia dinh dưỡng mới"""
-    if request.method == 'POST':
-        # Xử lý dữ liệu form
-        firstName = request.form.get('firstName')
-        lastName = request.form.get('lastName')
-        status_str = request.form.get('status', '')
+    """Tạo chuyên gia dinh dưỡng mới sử dụng FlaskForm."""
+    if not current_user.is_admin:
+        flash('Bạn không có quyền thực hiện hành động này.', 'danger')
+        return redirect(url_for('dietitians.index'))
         
-        # Chuyển đổi status string thành enum
+    form = DietitianForm()
+    if form.validate_on_submit():
         try:
-            # Chuyển đổi sang chữ hoa để khớp với định nghĩa enum
-            status_upper = status_str.upper() if status_str else ''
-            # Xác thực giá trị enum
-            if status_upper not in [item.name for item in DietitianStatus]:
-                raise ValueError(f"Giá trị enum không hợp lệ: {status_upper}")
-            # Lấy enum object, không chuyển sang giá trị enum
-            status_enum = DietitianStatus[status_upper]
-        except (ValueError, KeyError) as e:
-            flash(f'Trạng thái không hợp lệ: {status_str} - Lỗi: {str(e)}', 'error')
-            return render_template('dietitians/new.html', status_options=DietitianStatus)
+            # Tạo User trước
+            new_user = User(
+                firstName=form.firstName.data,
+                lastName=form.lastName.data,
+                email=form.email.data,
+                role='Dietitian' # Mặc định là Dietitian
+            )
+            new_user.set_password(form.password.data) # Hash mật khẩu từ form
+            db.session.add(new_user)
+            db.session.flush() # Lấy userID sau khi add
 
-        # Tạo bản ghi mới
-        dietitian = Dietitian(
-            firstName=firstName,
-            lastName=lastName,
-            status=status_enum  # Sử dụng enum
-        )
-        
-        try:
-            db.session.add(dietitian)
+            # Tạo Dietitian profile và liên kết với User
+            new_dietitian = Dietitian(
+                user_id=new_user.userID, # Liên kết user ID
+                firstName=form.firstName.data, # Đồng bộ tên
+                lastName=form.lastName.data,   # Đồng bộ họ
+                email=form.email.data,         # Đồng bộ email
+                phone=form.phone.data,
+                specialization=form.specialization.data,
+                notes=form.notes.data,
+                status=DietitianStatus.AVAILABLE # Mặc định là AVAILABLE
+            )
+            db.session.add(new_dietitian)
             db.session.commit()
-            flash('Thêm chuyên gia dinh dưỡng thành công!', 'success')
-            return redirect(url_for('dietitians.show', id=dietitian.dietitianID))
+            
+            flash('Tạo tài khoản chuyên gia dinh dưỡng thành công!', 'success')
+            return redirect(url_for('dietitians.show', id=new_dietitian.dietitianID))
         except Exception as e:
             db.session.rollback()
-            flash(f'Lỗi: {str(e)}', 'error')
-    
-    return render_template('dietitians/new.html', status_options=DietitianStatus)
+            current_app.logger.error(f"Lỗi khi tạo tài khoản dietitian: {e}")
+            flash(f'Lỗi khi tạo tài khoản: {str(e)}', 'error')
+            
+    # GET request hoặc form validation thất bại
+    return render_template('dietitians/new.html', form=form) # Truyền form vào template
 
 @dietitians_bp.route('/<int:id>/edit', methods=['GET', 'POST'])
 @login_required
@@ -266,7 +273,9 @@ def assign_patient(id, patient_id):
     return redirect(url_for('dietitians.show', id=id))
 
 @dietitians_bp.route('/<int:id>/unassign_patient/<int:patient_id>', methods=['POST'])
+@login_required
 def unassign_patient(id, patient_id):
+    """Bỏ phân công bệnh nhân"""
     try:
         patient = Patient.query.get_or_404(patient_id)
         if patient.dietitian_id == id:
diff --git a/app/routes/notifications.py b/app/routes/notifications.py
index 595d0d556b61c01b47e367f9052b34d208a477d6..4c920afe61c855aab787d67147d4af06eaaf887f 100644
--- a/app/routes/notifications.py
+++ b/app/routes/notifications.py
@@ -81,8 +81,10 @@ def mark_notification_as_read(notification_id):
 def mark_all_notifications_as_read():
     """API endpoint to mark all unread notifications as read for the current user."""
     try:
+        # Sử dụng .update() để hiệu quả hơn khi cập nhật nhiều bản ghi
         updated_count = Notification.query.filter_by(user_id=current_user.userID, is_read=False).update({'is_read': True})
         db.session.commit()
+        current_app.logger.info(f"Marked {updated_count} notifications as read for user {current_user.userID}")
         return jsonify({'success': True, 'message': f'{updated_count} notifications marked as read.'})
     except Exception as e:
         db.session.rollback()
diff --git a/app/routes/patients.py b/app/routes/patients.py
index 07796b4bb8f59100e901588a4e7bc840f600e71d..c8874410f71606edd15d1d586e3aae69b980b6cb 100644
--- a/app/routes/patients.py
+++ b/app/routes/patients.py
@@ -7,7 +7,7 @@ from app.models.encounter import Encounter, EncounterStatus
 from app.models.measurement import PhysiologicalMeasurement
 from app.models.referral import Referral, ReferralStatus
 from app.models.procedure import Procedure
-from app.models.report import Report
+from app.models.report import Report, ReportStatus
 from app.models.dietitian import Dietitian
 from sqlalchemy import desc, or_, func
 from sqlalchemy.orm import joinedload
@@ -27,6 +27,10 @@ from sklearn.ensemble import RandomForestClassifier
 from app.models.user import User
 from app.models.activity_log import ActivityLog
 from app.models.notification import Notification
+from wtforms import ValidationError
+# Import notification helpers
+from app.utils.notifications_helper import create_notification, create_notification_for_admins
+from app.models.patient_dietitian_assignment import PatientDietitianAssignment
 
 patients_bp = Blueprint('patients', __name__, url_prefix='/patients')
 
@@ -194,7 +198,8 @@ def index():
         current_page=page,
         per_page=per_page,
         pagination=pagination,
-        EmptyForm=EmptyForm
+        EmptyForm=EmptyForm,
+        PatientStatus=PatientStatus # Thêm PatientStatus vào context
     )
 
 @patients_bp.route('/<string:patient_id>')
@@ -237,7 +242,8 @@ def patient_detail(patient_id):
     ).all()
     reports = Report.query.filter_by(patient_id=patient_id).options(
         joinedload(Report.author), # Tải sẵn thông tin người tạo
-        joinedload(Report.encounter) # Thêm joinedload cho encounter
+        joinedload(Report.encounter), # Thêm joinedload cho encounter
+        joinedload(Report.dietitian) # THÊM: Tải sẵn thông tin dietitian được gán cho report
     ).order_by(
         desc(Report.report_date)
     ).all()
@@ -249,22 +255,40 @@ def patient_detail(patient_id):
         desc(Encounter.start_time)
     ).all()
     
+    # Debug để kiểm tra dietitian relationship đã được load đúng chưa
+    if all_encounters:
+        if all_encounters[0].dietitian_id:
+            current_app.logger.info(f"First encounter has dietitian_id={all_encounters[0].dietitian_id}, assigned_dietitian is {all_encounters[0].assigned_dietitian}")
+        else:
+            current_app.logger.info(f"First encounter has no dietitian_id")
+    
     latest_encounter = all_encounters[0] if all_encounters else None
 
     # Chuẩn bị dữ liệu encounters để hiển thị, bao gồm status display mới
     encounters_data = []
-    for enc in all_encounters:
+    for enc_base in all_encounters: # Đổi tên biến lặp để tránh nhầm lẫn
+        # Thay vì refresh, query lại encounter để đảm bảo dữ liệu mới nhất
+        enc = Encounter.query.options(joinedload(Encounter.assigned_dietitian)).get(enc_base.encounterID)
+        if not enc:
+            # Bỏ qua nếu encounter bị xóa giữa chừng (trường hợp hiếm)
+            current_app.logger.warning(f"Encounter ID {enc_base.encounterID} not found during data preparation for patient detail view.")
+            continue
+            
         # Sử dụng hàm helper đã cập nhật để lấy thông tin hiển thị status
         status_display = get_encounter_status_display(enc.status)
         # TODO: Xác định các chỉ số bất ổn
         # Logic này cần định nghĩa ngưỡng cho từng chỉ số
         unstable_metrics = [] # Placeholder
         encounters_data.append({
-            'encounter': enc,
+            'encounter': enc, # Dùng encounter đã query lại
             'status': status_display,
             'unstable_metrics': unstable_metrics
         })
-
+    
+    # Debug log lại số lượng encounters với assigned_dietitian
+    encounters_with_dietitian = sum(1 for data in encounters_data if data['encounter'].assigned_dietitian is not None)
+    current_app.logger.info(f"Prepared {len(encounters_data)} encounters for display, {encounters_with_dietitian} with assigned dietitian")
+    
     # Lấy danh sách dietitian cho modal (nếu cần)
     dietitians = User.query.filter_by(role='Dietitian').options(
         joinedload(User.dietitian) # Sửa lại tên relationship từ dietitian_profile thành dietitian
@@ -338,10 +362,27 @@ def new_patient():
             new_patient.bmi = new_patient.calculate_bmi()
         
         db.session.add(new_patient)
-        db.session.commit()
+        db.session.flush() # Lấy ID cho log và link
+        
+        # Log activity
+        activity = ActivityLog(
+            user_id=current_user.userID,
+            action=f"New patient {new_patient.full_name} (ID: {new_patient.id}) added by {current_user.full_name}.",
+            details=f"Patient ID: {new_patient.id}, Encounter ID: {new_patient.encounterID}"
+        )
+        db.session.add(activity)
         
-        flash('Patient has been added successfully.', 'success')
-        return redirect(url_for('patients.patient_detail', patient_id=new_patient.patient_id))
+        # *** Thêm Notification cho Admins ***
+        create_notification_for_admins(
+            message=f"New patient {new_patient.full_name} (ID: {new_patient.id}) added by {current_user.full_name}.",
+            link=('patients.patient_detail', {'patient_id': new_patient.id}),
+            exclude_user_id=current_user.userID
+        )
+        # *** Kết thúc Notification ***
+        
+        db.session.commit()
+        flash(f'Patient {new_patient.full_name} created successfully!', 'success')
+        return redirect(url_for('patients.patient_detail', patient_id=new_patient.id))
     
     return render_template('new_patient.html')
 
@@ -761,7 +802,20 @@ def delete_physical_measurement(patient_id, measurement_id):
 def encounter_measurements(patient_id, encounter_id):
     """Hiển thị chi tiết các phép đo cho một encounter cụ thể."""
     patient = Patient.query.get_or_404(patient_id)
-    encounter = Encounter.query.filter_by(encounterID=encounter_id, patient_id=patient_id).first_or_404()
+    
+    # Sửa lại query để include joinedload cho assigned_dietitian
+    encounter = Encounter.query.options(
+        joinedload(Encounter.assigned_dietitian)
+    ).filter_by(
+        encounterID=encounter_id, 
+        patient_id=patient_id
+    ).first_or_404()
+    
+    # Debug dietitian
+    if encounter.dietitian_id:
+        current_app.logger.info(f"Encounter {encounter_id} has dietitian_id={encounter.dietitian_id}, assigned_dietitian={encounter.assigned_dietitian}")
+    else:
+        current_app.logger.info(f"Encounter {encounter_id} has no dietitian_id")
 
     # Lấy tất cả measurements cho encounter này, sắp xếp theo thời gian
     measurements_query = PhysiologicalMeasurement.query.filter_by(
@@ -1027,565 +1081,630 @@ def upload_encounter_measurements_csv(patient_id, encounter_id):
 @patients_bp.route('/<string:patient_id>/encounters/new', methods=['POST'])
 @login_required
 def new_encounter(patient_id):
-    """Creates a new encounter for the patient, handling interruptions."""
     patient = Patient.query.get_or_404(patient_id)
-    
-    # Kiểm tra xem có encounter đang ON_GOING không
-    ongoing_encounter = Encounter.query.filter_by(
-        patient_id=patient.id,
-        status=EncounterStatus.ON_GOING
-    ).first()
-
-    # Kiểm tra xem có encounter đang NOT_STARTED không
-    latest_encounter = Encounter.query.filter_by(
-        patient_id=patient.id
-    ).order_by(Encounter.start_time.desc()).first()
-
-    # Không cho phép tạo encounter mới nếu encounter gần nhất đang NOT_STARTED
-    if latest_encounter and latest_encounter.status == EncounterStatus.NOT_STARTED:
-        flash(f"Cannot create a new encounter while the latest encounter ({latest_encounter.custom_encounter_id or latest_encounter.encounterID}) is still not started.", "warning")
-        return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
-
-    interrupted_report_details = None # Biến để lưu chi tiết report bị interrupt
-
-    if ongoing_encounter:
-        current_app.logger.warning(f"Attempt to create new encounter for patient {patient_id} while encounter {ongoing_encounter.custom_encounter_id or ongoing_encounter.encounterID} is ON_GOING.")
-        if current_user.is_admin:
-            # Admin đang cố interrupt -> Tiến hành interrupt encounter cũ
-            try:
-                ongoing_encounter.status = EncounterStatus.FINISHED
-                ongoing_encounter.end_time = datetime.utcnow()
-                interrupted_report_details = "Interrupted by admin."
-                current_app.logger.info(f"Admin {current_user.userID} interrupted encounter {ongoing_encounter.custom_encounter_id or ongoing_encounter.encounterID}. Setting status to FINISHED.")
-                
-                # Tìm report tương ứng để cập nhật
-                report_to_interrupt = Report.query.filter_by(encounter_id=ongoing_encounter.encounterID).first()
-                if report_to_interrupt:
-                    report_to_interrupt.status = 'Completed' # Đánh dấu report là hoàn thành
-                    report_to_interrupt.completed_date = datetime.utcnow()
-                    # Ghi chú vào assessment_details
-                    report_to_interrupt.assessment_details += "\n\n" + interrupted_report_details if report_to_interrupt.assessment_details else interrupted_report_details
-                    current_app.logger.info(f"Report {report_to_interrupt.id} for interrupted encounter marked as Completed.")
-                else:
-                     current_app.logger.warning(f"Could not find report associated with interrupted encounter {ongoing_encounter.encounterID} to mark as completed.")
-
-                # --- Thêm: Tìm và cập nhật Referral liên quan --- 
-                referral_to_complete = Referral.query.filter_by(encounter_id=ongoing_encounter.encounterID).first()
-                if referral_to_complete:
-                    referral_to_complete.referral_status = ReferralStatus.COMPLETED
-                    referral_to_complete.referralCompletedDateTime = datetime.utcnow()
-                    current_app.logger.info(f"Referral {referral_to_complete.id} for interrupted encounter marked as COMPLETED.")
-                else:
-                    current_app.logger.warning(f"Could not find referral associated with interrupted encounter {ongoing_encounter.encounterID} to mark as completed.")
-                # -----------------------------------------------
-
-                # Commit thay đổi trạng thái encounter/report/referral cũ NGAY LẬP TỨC
-                db.session.commit()
-                flash(f"Interrupted previous encounter ({ongoing_encounter.custom_encounter_id or ongoing_encounter.encounterID}). Marked as Finished.", "warning")
-            except Exception as interrupt_err:
-                db.session.rollback()
-                current_app.logger.error(f"Error interrupting encounter {ongoing_encounter.encounterID}: {interrupt_err}", exc_info=True)
-                flash(f"Error interrupting previous encounter: {interrupt_err}", "danger")
-                return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
-        else:
-            # Dietitian đang cố tạo mới -> Ngăn chặn (JS đã xử lý redirect)
-            # Vẫn nên có check ở backend để an toàn
-            flash("Please complete the report for the current encounter before creating a new one.", "warning")
-            # Tìm report ID để redirect (nếu có)
-            report_id_to_edit = db.session.query(Report.id).filter_by(encounter_id=ongoing_encounter.encounterID).scalar()
-            if report_id_to_edit:
-                return redirect(url_for('report.edit_report', report_id=report_id_to_edit))
-            else:
+    form = EmptyForm()
+    if form.validate_on_submit():
+        try: # Khối try chính
+            # Check if there is an ON_GOING encounter
+            latest_encounter = Encounter.query.filter_by(patient_id=patient.id).order_by(desc(Encounter.start_time)).first()
+            if latest_encounter and latest_encounter.status == EncounterStatus.ON_GOING:
+                flash('Cannot create a new encounter while another is ongoing.', 'warning')
+                # Redirect immediately if ongoing encounter exists
                 return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
 
-    # --- Tạo encounter mới (nếu không có lỗi hoặc sau khi interrupt) --- 
-    try:
-        # 1. Đếm số encounter hiện có của bệnh nhân này để xác định số thứ tự tiếp theo
-        existing_encounter_count = Encounter.query.filter_by(patient_id=patient.id).count()
-        next_sequence_num = existing_encounter_count + 1
-
-        # 2. Tạo custom_encounter_id
-        patient_id_part = patient.id[-5:] if patient.id and patient.id.startswith('P-') and len(patient.id) >= 7 else patient.id
-        custom_id = f"E-{patient_id_part}-{next_sequence_num:02d}"
-
-        while Encounter.query.filter_by(custom_encounter_id=custom_id).first():
-            current_app.logger.warning(f"Generated custom encounter ID {custom_id} already exists. Incrementing sequence.")
-            next_sequence_num += 1
-            custom_id = f"E-{patient_id_part}-{next_sequence_num:02d}"
-
-        # 3. Create a new encounter instance
-        new_encounter_obj = Encounter(
-            patient_id=patient.id,
-            start_time=datetime.utcnow(),
-            status=EncounterStatus.NOT_STARTED, # Bắt đầu là NOT_STARTED
-            custom_encounter_id=custom_id
-        )
-        db.session.add(new_encounter_obj)
-        # Commit để lấy ID cho encounter mới
-        db.session.flush() 
-        new_encounter_id = new_encounter_obj.encounterID
-        new_custom_id = new_encounter_obj.custom_encounter_id
-        current_app.logger.info(f"New encounter created: {new_custom_id} (PK: {new_encounter_id}) for patient {patient_id}")
+            # --- Generate Custom Encounter ID --- 
+            # (Logic for generating ID remains the same)
+            patient_id_number = patient.id.split('-')[-1] if '-' in patient.id else patient.id
+            last_custom_encounter = Encounter.query.filter(
+                Encounter.patient_id == patient.id,
+                Encounter.custom_encounter_id.like(f'E-{patient_id_number}-%')
+            ).order_by(desc(Encounter.custom_encounter_id)).first()
+
+            next_seq = 1
+            if last_custom_encounter and last_custom_encounter.custom_encounter_id:
+                try: # Khối try nhỏ cho việc parse sequence
+                    last_seq_str = last_custom_encounter.custom_encounter_id.split('-')[-1]
+                    next_seq = int(last_seq_str) + 1
+                except (IndexError, ValueError):
+                    current_app.logger.warning(f"Could not parse sequence from last custom encounter ID '{last_custom_encounter.custom_encounter_id}'. Starting sequence at 1.")
+                    next_seq = 1
+            
+            new_custom_id = f"E-{patient_id_number}-{next_seq:02d}"
+            # --- End Generate Custom Encounter ID ---
 
-        # 4. Cập nhật trạng thái patient dựa trên encounter mới nhất và các referral
-        update_patient_status_from_encounters(patient)
+            # Create new Encounter with custom ID
+            new_encounter = Encounter(
+                patient_id=patient.id,
+                start_time=datetime.utcnow(),
+                custom_encounter_id=new_custom_id
+            )
+            db.session.add(new_encounter)
+            db.session.flush() # Get encounterID for logs/links
+
+            # Log activity with custom ID
+            activity = ActivityLog(
+                user_id=current_user.userID,
+                action=f"Created new encounter {new_custom_id} (ID: {new_encounter.encounterID}) for patient {patient.full_name}",
+                details=f"Patient ID: {patient.id}, Encounter ID: {new_encounter.encounterID}, Custom ID: {new_custom_id}"
+            )
+            db.session.add(activity)
 
-        # Commit encounter mới và thay đổi trạng thái patient
-        db.session.commit()
-        flash(f'New encounter {new_custom_id} created successfully.', 'success')
-        # Redirect to the new encounter's measurement page
-        return redirect(url_for('patients.encounter_measurements', patient_id=patient.id, encounter_id=new_encounter_id))
+            # Update patient status
+            update_patient_status_from_encounters(patient)
 
-    except Exception as e:
-        db.session.rollback()
-        current_app.logger.error(f"Error creating new encounter for patient {patient.id} after check/interrupt: {str(e)}", exc_info=True)
-        flash(f'Error creating new encounter: {str(e)}', 'error')
-        return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
+            # Add Notification for New Encounter
+            encounter_display_id = new_encounter.custom_encounter_id
+            admin_message = f"New encounter ({encounter_display_id}) created for patient {patient.full_name} by {current_user.full_name}."
+            link_kwargs = {'patient_id': patient.id, 'encounter_id': new_encounter.encounterID}
+            admin_link = ('patients.encounter_measurements', link_kwargs)
+            create_notification_for_admins(admin_message, admin_link, exclude_user_id=current_user.userID)
+            
+            # Notify assigned dietitian
+            if patient.assigned_dietitian_user_id and patient.assigned_dietitian_user_id != current_user.userID:
+                dietitian_message = f"New encounter ({encounter_display_id}) created for your patient {patient.full_name}."
+                create_notification(patient.assigned_dietitian_user_id, dietitian_message, admin_link)
+
+            # Commit everything at the end of the try block
+            db.session.commit() 
+            flash('New encounter created successfully.', 'success') 
+            # Redirect after successful commit inside try
+            return redirect(url_for('patients.encounter_measurements', patient_id=patient.id, encounter_id=new_encounter.encounterID))
+
+        except Exception as e: # Khối except tương ứng, cùng mức thụt lề với try
+            db.session.rollback() 
+            current_app.logger.error(f"Error creating new encounter for patient {patient.id}: {str(e)}", exc_info=True) 
+            flash(f'Error creating encounter: {str(e)}', 'danger') 
+            # Redirect on error
+            return redirect(url_for('patients.patient_detail', patient_id=patient.id))
+    else:
+        # Handle CSRF or other form validation errors
+        flash('Invalid request to create encounter.', 'danger')
+        return redirect(url_for('patients.patient_detail', patient_id=patient.id))
 
 def update_patient_status_from_encounters(patient):
-    # ... (docstring và khởi tạo biến như cũ) ...
-    old_status_val = patient.status.value if patient.status else "None"
-    old_dietitian_id = patient.assigned_dietitian_user_id # Lưu lại dietitian cũ để log
-    new_status = None
-    current_app.logger.info(f"[UpdateStatus] Patient {patient.id}: Starting update. Current status: {old_status_val}, Assigned Dietitian: {old_dietitian_id}")
-
-    # 1. Lấy encounter mới nhất
-    latest_encounter = Encounter.query.filter_by(
-        patient_id=patient.id
-    ).order_by(Encounter.start_time.desc()).first()
-
-    # 2. Kiểm tra referral đang chờ (DIETITIAN_UNASSIGNED) cho encounter mới nhất (nếu có)
-    pending_referral_for_latest = None
-    if latest_encounter:
-        pending_referral_for_latest = Referral.query.filter_by(
-            encounter_id=latest_encounter.encounterID,
-            referral_status=ReferralStatus.DIETITIAN_UNASSIGNED
-        ).first()
-
-    # 3. Logic xác định trạng thái mới
+    """Update patient status based on encounters and referrals. Also handles status change notifications."""
+    latest_encounter = Encounter.query.filter_by(patient_id=patient.id).order_by(desc(Encounter.start_time)).first()
+    
+    # Xác định trạng thái mới dựa trên logic ưu tiên
+    new_status = PatientStatus.NOT_ASSESSED # Mặc định là chưa đánh giá
+    
     if latest_encounter:
-        # a. Encounter FINISHED -> Patient COMPLETED
-        if latest_encounter.status == EncounterStatus.FINISHED:
+        # Ưu tiên 1: Encounter chưa bắt đầu -> Patient Not Assessed
+        if latest_encounter.status == EncounterStatus.NOT_STARTED:
+            new_status = PatientStatus.NOT_ASSESSED
+        
+        # Ưu tiên 2: Encounter đã kết thúc -> Patient Completed
+        elif latest_encounter.status == EncounterStatus.FINISHED:
+            # TODO: Có thể cần kiểm tra thêm các yếu tố khác trước khi kết luận Completed
             new_status = PatientStatus.COMPLETED
-            current_app.logger.debug(f"Patient {patient.id}: Setting status to COMPLETED (latest encounter {latest_encounter.encounterID} FINISHED).")
-        # b. Encounter ON_GOING 
+        
+        # Ưu tiên 3: Encounter đang diễn ra -> Kiểm tra Referral
         elif latest_encounter.status == EncounterStatus.ON_GOING:
-            # *SỬA*: Nếu có referral đang chờ gán -> NEEDS_ASSESSMENT
-            if pending_referral_for_latest:
-                new_status = PatientStatus.NEEDS_ASSESSMENT
-                current_app.logger.debug(f"Patient {patient.id}: Setting status to NEEDS_ASSESSMENT (latest encounter {latest_encounter.encounterID} ON_GOING with pending referral {pending_referral_for_latest.id}).")
-            # Nếu không có referral chờ -> ASSESSMENT_IN_PROGRESS (giả định đã có dietitian gán từ trước đó hoặc luồng khác)
+            latest_referral = Referral.query.filter_by(encounter_id=latest_encounter.encounterID).order_by(desc(Referral.referralRequestedDateTime)).first()
+            if latest_referral:
+                if latest_referral.referral_status == ReferralStatus.DIETITIAN_UNASSIGNED:
+                    new_status = PatientStatus.NEEDS_ASSESSMENT
+                elif latest_referral.referral_status == ReferralStatus.WAITING_FOR_REPORT:
+                    new_status = PatientStatus.ASSESSMENT_IN_PROGRESS
+                elif latest_referral.referral_status == ReferralStatus.COMPLETED:
+                    # Encounter đang diễn ra nhưng referral đã xong? -> Vẫn là IN_PROGRESS
+                    new_status = PatientStatus.ASSESSMENT_IN_PROGRESS
             else:
+                # Encounter đang diễn ra nhưng không có referral -> ASSESSMENT_IN_PROGRESS
                 new_status = PatientStatus.ASSESSMENT_IN_PROGRESS 
-                current_app.logger.debug(f"Patient {patient.id}: Setting status to ASSESSMENT_IN_PROGRESS (latest encounter {latest_encounter.encounterID} ON_GOING, no pending referral found).")
-        # c. Encounter NOT_STARTED -> Patient NOT_ASSESSED
-        elif latest_encounter.status == EncounterStatus.NOT_STARTED:
-            new_status = PatientStatus.NOT_ASSESSED 
-            current_app.logger.debug(f"Patient {patient.id}: Setting status to NOT_ASSESSED (latest encounter {latest_encounter.encounterID} NOT_STARTED).")
-        else: # Trạng thái encounter không xác định
-            new_status = PatientStatus.NOT_ASSESSED # Mặc định an toàn
-            current_app.logger.warning(f"Patient {patient.id}: Latest encounter {latest_encounter.encounterID} has unexpected status {latest_encounter.status}. Setting patient status to NOT_ASSESSED.")
-    # 4. Không có encounter nào
-    else:
-        # Kiểm tra có referral nào đang chờ gán không (không cần liên kết với encounter cụ thể nữa)
-        any_pending_referral = Referral.query.filter_by(
-            patient_id=patient.id,
-            referral_status=ReferralStatus.DIETITIAN_UNASSIGNED
-        ).first()
-        if any_pending_referral:
-            new_status = PatientStatus.NEEDS_ASSESSMENT
-            current_app.logger.debug(f"Patient {patient.id}: Setting status to NEEDS_ASSESSMENT (no encounters, but found pending referral {any_pending_referral.id}).")
-        else:
-            new_status = PatientStatus.NOT_ASSESSED
-            current_app.logger.debug(f"Patient {patient.id}: Setting status to NOT_ASSESSED (no encounters, no pending referrals).")
-
-    # 5. Cập nhật trạng thái bệnh nhân và dietitian nếu cần
-    status_changed = False
-    dietitian_removed = False
 
-    if new_status and (not patient.status or patient.status != new_status):
+    # So sánh và cập nhật trạng thái nếu có thay đổi
+    old_status = patient.status
+    if old_status != new_status:
         patient.status = new_status
-        status_changed = True
-        current_app.logger.info(f"[UpdateStatus] Patient {patient.id} status WILL BE updated to {patient.status.value}")
-    elif not new_status:
-        current_app.logger.error(f"[UpdateStatus] Patient {patient.id}: Could not determine new status.")
-
-    # *** Quan trọng: Reset Dietitian nếu trạng thái KHÔNG phải là ASSESSMENT_IN_PROGRESS hoặc COMPLETED *** 
-    # Giữ lại dietitian khi bệnh nhân đang được đánh giá hoặc đã hoàn thành
-    if new_status not in [PatientStatus.ASSESSMENT_IN_PROGRESS, PatientStatus.COMPLETED] and patient.assigned_dietitian_user_id is not None:
-        patient.assigned_dietitian_user_id = None
-        patient.assignment_date = None # Reset cả ngày gán
-        dietitian_removed = True
-        current_app.logger.info(f"[UpdateStatus] Patient {patient.id}: Dietitian assignment WILL BE removed because new status is {new_status.value if new_status else 'None'}.")
-    else:
-        # Ghi log nếu dietitian được giữ lại
-        if patient.assigned_dietitian_user_id is not None and (new_status == PatientStatus.ASSESSMENT_IN_PROGRESS or new_status == PatientStatus.COMPLETED):
-             current_app.logger.info(f"[UpdateStatus] Patient {patient.id}: Dietitian assignment ({patient.assigned_dietitian_user_id}) RETAINED for status {new_status.value}")
+        db.session.add(patient)
+        current_app.logger.info(f"Patient {patient.id} status updated from {old_status} to {new_status} by update_patient_status_from_encounters.")
 
-    if not status_changed and not dietitian_removed:
-         current_app.logger.info(f"[UpdateStatus] Patient {patient.id}: No changes to status ({old_status_val}) or dietitian assignment ({old_dietitian_id}).")
+        # *** Add Notification for Status Change ***
+        status_change_message = f"Patient {patient.full_name} status changed to {new_status.value}."
+        status_link = ('patients.patient_detail', {'patient_id': patient.id})
+        
+        # Notify Admins
+        create_notification_for_admins(status_change_message, status_link)
+        
+        # Notify assigned dietitian (if exists)
+        if patient.assigned_dietitian_user_id:
+             create_notification(patient.assigned_dietitian_user_id, status_change_message, status_link)
+        # *** End Notification Status Change ***
+    else:
+         current_app.logger.debug(f"Patient {patient.id} status remains {old_status}.")
 
-    current_app.logger.info(f"[UpdateStatus] Patient {patient.id}: Finished update logic. Final status decided: {new_status.value if new_status else 'None'}. Dietitian ID decided: {patient.assigned_dietitian_user_id}")
-    return patient.status # Trả về trạng thái mới (object patient đã được thay đổi trực tiếp)
+    return new_status # Return the new (or unchanged) status
 
-# Thêm route xóa encounter
 @patients_bp.route('/<string:patient_id>/encounters/<int:encounter_pk>/delete', methods=['POST'])
 @login_required
 def delete_encounter(patient_id, encounter_pk):
-    """Deletes an encounter and its associated measurements/report/referral, updating patient status and dietitian assignment if necessary."""
-    encounter = Encounter.query.get_or_404(encounter_pk)
+    form = EmptyForm() 
     patient = Patient.query.get_or_404(patient_id)
+    encounter_to_delete = Encounter.query.filter_by(encounterID=encounter_pk, patient_id=patient.id).first_or_404()
 
-    if encounter.patient_id != patient.id:
-        flash('Invalid encounter for this patient.', 'error')
+    if not current_user.is_admin:
+        flash('Permission denied.', 'error')
         return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
 
-    form = EmptyForm()
-    if not form.validate_on_submit():
-        flash('Invalid CSRF token. Please try again.', 'error')
-        return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
+    if form.validate_on_submit():
+        try: # CORRECT INDENTATION FOR TRY BLOCK
+            # Store info before deleting
+            deleted_encounter_id = encounter_to_delete.encounterID
+            deleted_encounter_display_id = encounter_to_delete.custom_encounter_id or deleted_encounter_id
+            patient_full_name = patient.full_name
+            dietitian_id_assigned = encounter_to_delete.dietitian_id
+            
+            # Delete related records (Measurements, Referrals, Reports, Procedures for THIS encounter)
+            # ... (Add deletion logic here if needed)
+            
+            db.session.delete(encounter_to_delete)
+            
+            # Log activity
+            activity = ActivityLog(
+                user_id=current_user.userID,
+                action=f"Deleted encounter {deleted_encounter_display_id} for patient {patient_full_name}",
+                details=f"Patient ID: {patient_id}, Encounter ID: {deleted_encounter_id}"
+            )
+            db.session.add(activity)
+            
+            # Update patient status AFTER deleting the encounter
+            update_patient_status_from_encounters(patient)
 
-    try:
-        custom_id_for_flash = encounter.custom_encounter_id if encounter.custom_encounter_id else f"(PK: {encounter.encounterID})"
-        
-        # --- Hủy liên kết Dietitian nếu cần --- 
-        # Lấy danh sách referrals của encounter này từ bảng Referral
-        encounter_referrals = Referral.query.filter_by(encounter_id=encounter.encounterID).all()
-        
-        # Kiểm tra xem việc gán dietitian hiện tại có phải là do referral của encounter này không
-        referral_linked_to_assignment = None
-        if patient.assigned_dietitian_user_id and encounter_referrals:
-            # Giả định chỉ có một referral / encounter
-            # Và việc gán dietitian được lưu trong patient.assigned_dietitian_user_id
-            # Kiểm tra xem dietitian được gán cho bệnh nhân có phải là dietitian của referral này không
-            encounter_referral = encounter_referrals[0] # Lấy referral đầu tiên
-            if encounter_referral.assigned_dietitian_user_id == patient.assigned_dietitian_user_id:
-                 # Nếu dietitian gán cho patient trùng với dietitian của referral sắp bị xóa
-                 referral_linked_to_assignment = encounter_referral # Lưu lại để biết cần hủy gán
-
-        # --- Xóa các đối tượng liên quan --- 
-        # Tìm và xóa Report liên quan
-        report_to_delete = Report.query.filter_by(encounter_id=encounter.encounterID).first()
-        if report_to_delete:
-            db.session.delete(report_to_delete)
-            current_app.logger.info(f"Deleted report {report_to_delete.id} associated with encounter {encounter_pk}.")
+            # Add Notification for Encounter Deletion
+            admin_message = f"Encounter ({deleted_encounter_display_id}) for patient {patient_full_name} deleted by {current_user.full_name}."
+            admin_link = ('patients.patient_detail', {'patient_id': patient.id, '_anchor': 'encounters'})
+            create_notification_for_admins(admin_message, admin_link, exclude_user_id=current_user.userID)
             
-        # Tìm và xóa Referral liên quan (nếu có)
-        # Sử dụng encounter_referrals đã query ở trên
-        if encounter_referrals:
-            for ref in encounter_referrals:
-                 db.session.delete(ref)
-                 current_app.logger.info(f"Deleted referral {ref.id} associated with encounter {encounter_pk}.")
-
-        # Xóa encounter (và measurements do cascade)
-        db.session.delete(encounter)
-        current_app.logger.info(f"Deleted encounter {encounter_pk}.")
-
-        # --- Cập nhật Patient --- 
-        # Hủy gán dietitian NẾU referral liên quan bị xóa (Thực hiện TRƯỚC khi cập nhật status)
-        if referral_linked_to_assignment:
-             patient.assigned_dietitian_user_id = None
-             patient.assessment_date = None # Reset ngày đánh giá
-             current_app.logger.info(f"Unassigned dietitian from patient {patient.id} because associated referral {referral_linked_to_assignment.id} was deleted.")
-             # Flash message này nên hiển thị sau khi commit thành công
-             # flash('Dietitian assignment removed as the related encounter/referral was deleted.', 'warning')
-
-        # Cập nhật trạng thái của bệnh nhân dựa trên encounter còn lại và referrals
-        new_status = update_patient_status_from_encounters(patient)
-        
-        # Commit tất cả thay đổi vào DB
-        db.session.commit()
-        
-        # Flash messages sau khi commit thành công
-        flash(f'Encounter {custom_id_for_flash} deleted successfully.', 'success')
-        if referral_linked_to_assignment: # Flash message hủy gán dietitian ở đây
-             flash('Dietitian assignment removed as the related encounter/referral was deleted.', 'warning')
-        if hasattr(patient, 'status') and patient.status == new_status: # Kiểm tra xem status đã được gán chưa
-             flash(f'Patient status is now {new_status.value}.', 'info')
+            # Notify the dietitian who WAS assigned to THIS encounter
+            if dietitian_id_assigned and dietitian_id_assigned != current_user.userID:
+                 dietitian_message = f"Encounter ({deleted_encounter_display_id}) for patient {patient_full_name} (which you were assigned to) was deleted."
+                 create_notification(dietitian_id_assigned, dietitian_message, admin_link)
+
+            # --- THÊM LOGIC HỦY ASSIGNMENT --- 
+            if dietitian_id_assigned: # Nếu encounter bị xóa có gán dietitian
+                active_assignment = PatientDietitianAssignment.get_active_assignment(patient.id)
+                # Chỉ hủy assignment nếu dietitian của assignment đang active trùng với dietitian của encounter bị xóa
+                if active_assignment and active_assignment.dietitian_id == dietitian_id_assigned:
+                    current_app.logger.info(f"Deactivating assignment {active_assignment.id} for patient {patient.id} due to deletion of encounter {deleted_encounter_id}")
+                    active_assignment.is_active = False
+                    active_assignment.end_date = datetime.utcnow()
+                    db.session.add(active_assignment)
+                    # Gửi thêm thông báo về việc hủy assignment
+                    deactivation_message = f"Your assignment to patient {patient.full_name} was deactivated because the related encounter was deleted."
+                    deactivation_link = ('patients.patient_detail', {'patient_id': patient.id})
+                    create_notification(dietitian_id_assigned, deactivation_message, deactivation_link)
+                elif active_assignment:
+                    current_app.logger.info(f"Deleted encounter {deleted_encounter_id} had dietitian {dietitian_id_assigned}, but active assignment {active_assignment.id} is for dietitian {active_assignment.dietitian_id}. Assignment not deactivated.")
+                else:
+                    current_app.logger.info(f"Deleted encounter {deleted_encounter_id} had dietitian {dietitian_id_assigned}, but no active assignment found for patient {patient.id}. Assignment not deactivated.")
+            # --- KẾT THÚC LOGIC HỦY ASSIGNMENT ---
+            
+            # Commit changes (bao gồm cả việc hủy assignment nếu có)
+            db.session.commit() # Sửa lỗi thụt lề
+            flash(f'Encounter {deleted_encounter_display_id} deleted successfully.', 'success') # Sửa lỗi thụt lề
 
-    except Exception as e:
-        db.session.rollback()
-        current_app.logger.error(f"Error deleting encounter {encounter_pk} for patient {patient.id}: {str(e)}", exc_info=True)
-        flash(f'Error deleting encounter: {str(e)}', 'error')
+        except Exception as e: # CORRECT INDENTATION FOR EXCEPT BLOCK
+            db.session.rollback() # Sửa lỗi thụt lề
+            current_app.logger.error(f"Error deleting encounter {encounter_pk}: {str(e)}", exc_info=True) # Sửa lỗi thụt lề
+            flash(f'Error deleting encounter: {str(e)}', 'error') # Sửa lỗi thụt lề
 
-    return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters'))
+        # Redirect outside the try...except block but inside the if form.validate_on_submit()
+        return redirect(url_for('patients.patient_detail', patient_id=patient.id, _anchor='encounters')) # Sửa lỗi thụt lề
+    else:
+        # Handle CSRF error
+        flash('Invalid request to delete encounter.', 'danger') # Sửa lỗi thụt lề
+        return redirect(url_for('patients.patient_detail', patient_id=patient_id, _anchor='encounters')) # Sửa lỗi thụt lề
 
 @patients_bp.route('/<string:patient_id>/encounter/<int:encounter_id>/run_ml', methods=['POST'])
 @login_required
 def run_encounter_ml(patient_id, encounter_id):
-    """Chạy dự đoán ML cho encounter và cập nhật trạng thái theo quy trình mới."""
-    patient = Patient.query.get_or_404(patient_id)
-    encounter = Encounter.query.filter_by(encounterID=encounter_id, patient_id=patient.id).first_or_404()
-    form = EmptyForm() # Sử dụng form rỗng để validate CSRF
-
-    if not form.validate_on_submit():
-        flash('Invalid CSRF token. Please try again.', 'danger')
-        return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id))
-
-    try:
-        # *** THÊM: Kiểm tra xem encounter có measurement chưa ***
-        measurement_count = PhysiologicalMeasurement.query.filter_by(encounter_id=encounter.encounterID).count()
-        if measurement_count == 0:
-            current_app.logger.warning(f"ML prediction skipped for encounter {encounter_id}: No measurements found.")
-            flash("No measurements found for this encounter. Please upload data before running the ML prediction.", "warning")
-            return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id))
-        # *** KẾT THÚC KIỂM TRA ***
-
-        # Cập nhật trạng thái encounter thành ON_GOING khi chạy ML
-        if encounter.status == EncounterStatus.NOT_STARTED:
-            encounter.status = EncounterStatus.ON_GOING
-            current_app.logger.info(f"Encounter {encounter_id} status updated to ON_GOING after running ML")
-        
-        # Chạy ML prediction và cập nhật encounter
-        needs_intervention_result = _run_ml_prediction(encounter)
-        
-        # Chỉ xử lý nếu kết quả ML không phải None (không có lỗi)
-        if needs_intervention_result is not None:
-            # Cập nhật encounter.needs_intervention
-            encounter.needs_intervention = needs_intervention_result
-
-            if needs_intervention_result:
-                # Nếu cần can thiệp, tạo referral nếu chưa có
-                existing_referral = Referral.query.filter_by(
-                    patient_id=patient.id,
-                    encounter_id=encounter.encounterID
-                ).first()
-                
-                if not existing_referral:
-                    new_referral = Referral(
-                        patient_id=patient.id,
-                        encounter_id=encounter.encounterID,
-                        # Sử dụng notes thay vì referral_reason
-                        notes="Algorithm detected need for assessment based on physiological measurements.", 
-                        referralRequestedDateTime=datetime.utcnow(),
-                        referral_status=ReferralStatus.DIETITIAN_UNASSIGNED,
-                        is_ml_recommended=True # Đánh dấu là do ML tạo
-                    )
-                    db.session.add(new_referral)
-                    current_app.logger.info(f"Created new ML-based referral for patient {patient_id}, encounter {encounter_id}")
-                    flash("ML prediction indicates intervention needed. A new referral has been created.", "info")
-                else:
-                    flash("ML prediction indicates intervention needed. An existing referral is already in place.", "info")
-            else:
-                # Nếu không cần can thiệp
-                flash("ML prediction indicates no intervention needed at this time.", "success")
-                # *** THÊM LOGIC CẬP NHẬT TRẠNG THÁI ENCOUNTER ***
-                if encounter.status == EncounterStatus.ON_GOING:
-                    encounter.status = EncounterStatus.FINISHED
-                    encounter.end_time = datetime.utcnow() # Ghi lại thời gian kết thúc
-                    current_app.logger.info(f"Encounter {encounter_id} status updated to FINISHED as ML indicated no intervention needed.")
-                # *** KẾT THÚC THÊM LOGIC ***
+    form = EmptyForm() # Add form for CSRF validation
+    if form.validate_on_submit():
+        try: # Start try block
+            # Fetch patient and encounter FIRST
+            patient = Patient.query.get_or_404(patient_id) # Sửa lỗi thụt lề
+            encounter = Encounter.query.filter_by(encounterID=encounter_id, patient_id=patient.id).first_or_404() # Sửa lỗi thụt lề
+
+            # Check if measurements exist (Example check)
+            if not encounter.measurements: # Sửa lỗi thụt lề
+                flash('No measurements found for this encounter. Cannot run prediction.', 'warning') # Sửa lỗi thụt lề
+                return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id)) # Sửa lỗi thụt lề
+
+            # Now you can safely access encounter
+            original_encounter_status = encounter.status # Sửa lỗi thụt lề
+            encounter.status = EncounterStatus.ON_GOING # Set to ON_GOING when ML runs # Sửa lỗi thụt lề
+            needs_intervention_result = _run_ml_prediction(encounter) # Sửa lỗi thụt lề
             
-            # Quan trọng: Cập nhật trạng thái bệnh nhân SAU KHI xử lý ML và Referral
-            update_patient_status_from_encounters(patient)
+            if needs_intervention_result is not None: # Sửa lỗi thụt lề
+                encounter.needs_intervention = needs_intervention_result # Sửa lỗi thụt lề
+                referral_created_by_ml = False # Sửa lỗi thụt lề
+
+                if needs_intervention_result: # Sửa lỗi thụt lề
+                    existing_referral = Referral.query.filter_by(patient_id=patient.id, encounter_id=encounter.encounterID).first() # Sửa lỗi thụt lề
+                    if not existing_referral: # Sửa lỗi thụt lề
+                        # Need to define referral details properly here
+                        new_referral = Referral( # Sửa lỗi thụt lề
+                            patient_id=patient.id, # Sửa lỗi thụt lề
+                            encounter_id=encounter.encounterID, # Sửa lỗi thụt lề
+                            notes="ML Prediction Recommended Assessment", # Sửa lỗi thụt lề
+                            referral_status=ReferralStatus.DIETITIAN_UNASSIGNED, # Sửa lỗi thụt lề
+                            referralRequestedDateTime=datetime.utcnow(), # Sửa lỗi thụt lề
+                            is_ml_recommended=True # Sửa lỗi thụt lề
+                        ) # Sửa lỗi thụt lề
+                        db.session.add(new_referral) # Sửa lỗi thụt lề
+                        db.session.flush() # Get referral ID before creating report # Sửa lỗi thụt lề
+                        referral_created_by_ml = True # Sửa lỗi thụt lề
+                        flash("ML prediction indicates intervention needed. A new referral has been created.", "info") # Sửa lỗi thụt lề
+
+                        # *** ADD AUTO-CREATE REPORT LOGIC HERE ***
+                        try: # Sửa lỗi thụt lề
+                            # Check if a report for this encounter already exists (e.g., from a previous failed attempt)
+                            existing_report = Report.query.filter_by(encounter_id=encounter.encounterID).first() # Sửa lỗi thụt lề
+                            if not existing_report: # Sửa lỗi thụt lề
+                                new_report = Report( # Sửa lỗi thụt lề
+                                    author_id=current_user.userID, # User who triggered ML # Sửa lỗi thụt lề
+                                    patient_id=patient.id, # Sửa lỗi thụt lề
+                                    encounter_id=encounter.encounterID, # Sửa lỗi thụt lề
+                                    referral_id=new_referral.id, # Link to the new referral # Sửa lỗi thụt lề
+                                    report_date=datetime.utcnow(), # Sửa lỗi thụt lề
+                                    report_type='initial_assessment', # Or a specific type like 'ml_triggered'? # Sửa lỗi thụt lề
+                                    status=ReportStatus.PENDING, # Đổi thành Pending # Sửa lỗi thụt lề
+                                    # Add minimal default text to required fields to pass potential validation
+                                    # Kiểm tra lại model Report xem các trường này có nullable=False không
+                                    nutrition_diagnosis="Assessment required based on ML prediction.", # Sửa lỗi thụt lề
+                                    assessment_details="Pending dietitian assessment.", # Sửa lỗi thụt lề
+                                    intervention_plan="Pending dietitian assessment.", # Sửa lỗi thụt lề
+                                    monitoring_plan="Pending dietitian assessment." # Sửa lỗi thụt lề
+                                ) # Sửa lỗi thụt lề
+                                db.session.add(new_report) # Sửa lỗi thụt lề
+                                current_app.logger.info(f"Auto-created draft report for encounter {encounter.encounterID} based on ML prediction.") # Sửa lỗi thụt lề
+                                # Optionally create notification for assigned dietitian/admin about the new DRAFT report?
+                            else: # Sửa lỗi thụt lề
+                                current_app.logger.warning(f"Report for encounter {encounter.encounterID} already exists, skipping auto-creation.") # Sửa lỗi thụt lề
+
+                        except Exception as report_error: # Sửa lỗi thụt lề
+                                # Log error but don't necessarily fail the whole ML process
+                                current_app.logger.error(f"Failed to auto-create report for encounter {encounter.encounterID}: {report_error}", exc_info=True) # Sửa lỗi thụt lề
+                                flash("Warning: Could not automatically create a draft report.", "warning") # Sửa lỗi thụt lề
+                        # *** END AUTO-CREATE REPORT LOGIC ***
+
+                        # Create Notifications for Referral
+                        admin_users = User.query.filter_by(role='admin').all() # Sửa lỗi thụt lề
+                        admin_ids = [admin.userID for admin in admin_users] # Sửa lỗi thụt lề
+                        # Tạo thông báo cho tất cả admin ngoại trừ người dùng hiện tại nếu họ là admin
+                        referral_message = f"New Referral for Patient {patient.full_name} (ML Recommended)" # Sửa lỗi thụt lề
+                        referral_link = ('referrals.view_referral', {'referral_id': new_referral.id}) # Sửa lỗi thụt lề
+                        # SỬA TÊN THAM SỐ Ở ĐÂY
+                        create_notification_for_admins( # Sửa lỗi thụt lề
+                            message=referral_message, # Sửa lỗi thụt lề
+                            link=referral_link, # Đổi link_tuple thành link # Sửa lỗi thụt lề
+                            exclude_user_id=current_user.userID if current_user.role == 'admin' else None # Sửa lỗi thụt lề
+                        ) # Sửa lỗi thụt lề
+                        # TODO: Consider notifying specific dietitians if needed, although referral is initially unassigned.
+
+                    else: # Sửa lỗi thụt lề
+                        flash("ML prediction indicates intervention needed. An existing referral is already in place.", "info") # Sửa lỗi thụt lề
+                else: # Khi không cần can thiệp # Sửa lỗi thụt lề
+                    flash("ML prediction indicates no intervention needed at this time.", "success") # Sửa lỗi thụt lề
+
+                    # Luôn cập nhật encounter thành FINISHED nếu nó chưa finished
+                    if encounter.status != EncounterStatus.FINISHED: # Sửa lỗi thụt lề
+                        encounter.status = EncounterStatus.FINISHED # Sửa lỗi thụt lề
+                        encounter.end_time = datetime.utcnow() # Sửa lỗi thụt lề
+                        current_app.logger.info(f"Encounter {encounter.encounterID} status updated to FINISHED as ML indicated no intervention needed.") # Sửa lỗi thụt lề
+                        db.session.add(encounter) # Add encounter to session if changed # Sửa lỗi thụt lề
+
+                        # Add Notification for Admins & Dietitian about Encounter FINISHED
+                        encounter_display_id = encounter.custom_encounter_id or encounter.encounterID # Sửa lỗi thụt lề
+                        finish_message = f"Encounter ({encounter_display_id}) for patient {patient.full_name} marked as FINISHED (ML)." # Sửa lỗi thụt lề
+                        finish_link = ('patients.encounter_measurements', {'patient_id': patient.id, 'encounter_id': encounter.encounterID}) # Sửa lỗi thụt lề
+                        create_notification_for_admins(finish_message, finish_link) # Sửa lỗi thụt lề
+                        if encounter.dietitian_id: # Sửa lỗi thụt lề
+                            create_notification(encounter.dietitian_id, finish_message, finish_link) # Sửa lỗi thụt lề
+
+                    # Kiểm tra và cập nhật Referral hiện có thành COMPLETED
+                    existing_referral = Referral.query.filter_by(encounter_id=encounter.encounterID).first() # Sửa lỗi thụt lề
+                    if existing_referral and existing_referral.referral_status != ReferralStatus.COMPLETED: # Sửa lỗi thụt lề
+                        current_app.logger.info(f"Updating existing referral {existing_referral.id} to COMPLETED for encounter {encounter.encounterID} as ML indicated no intervention.") # Sửa lỗi thụt lề
+                        existing_referral.referral_status = ReferralStatus.COMPLETED # Sửa lỗi thụt lề
+                        # Có thể thêm logic cập nhật referralCompletedDateTime nếu cần
+                        db.session.add(existing_referral) # Add referral to session if changed # Sửa lỗi thụt lề
+
+                # Cập nhật trạng thái bệnh nhân (sẽ tự xử lý logic COMPLETED dựa trên encounter FINISHED)
+                update_patient_status_from_encounters(patient) # Sửa lỗi thụt lề
+                db.session.commit() # Commit tất cả thay đổi (encounter, referral, report, patient status) # Sửa lỗi thụt lề
             
-            db.session.commit()
-        else:
-            # Có lỗi xảy ra trong quá trình chạy ML (_run_ml_prediction trả về None)
-            flash("An error occurred during ML prediction. Please check logs.", "danger")
-            # Không commit và redirect về trang encounter
+            else: # needs_intervention_result is None (lỗi ML) # Sửa lỗi thụt lề
+                flash("An error occurred during ML prediction. Please check logs.", "danger") # Sửa lỗi thụt lề
+                # Không commit nếu ML lỗi
 
-        return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id))
+            # Luôn redirect sau khi xử lý xong khối try (hoặc nếu ML lỗi)
+            return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id)) # Sửa lỗi thụt lề
     
-    except Exception as e:
-        db.session.rollback()
-        current_app.logger.error(f"Error running ML for encounter {encounter_id}: {str(e)}", exc_info=True)
-        flash(f"Error running ML prediction: {str(e)}", "danger")
-        return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id))
+        except Exception as e: # Sửa lỗi thụt lề
+            # ... (khối except) ...
+            db.session.rollback() # Rollback if any error occurs during the process # Sửa lỗi thụt lề
+            current_app.logger.error(f"Error running ML for encounter {encounter_id}: {str(e)}", exc_info=True) # Sửa lỗi thụt lề
+            flash(f"Error running ML prediction: {str(e)}", "danger") # Sửa lỗi thụt lề
+            return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id)) # Sửa lỗi thụt lề
+    else:
+        # Handle CSRF error
+        flash('Invalid request to run ML prediction.', 'danger') # Sửa lỗi thụt lề
+        return redirect(url_for('patients.encounter_measurements', patient_id=patient_id, encounter_id=encounter_id)) # Sửa lỗi thụt lề
 
 @patients_bp.route('/<string:patient_id>/delete', methods=['POST'])
 @login_required
 def delete_patient(patient_id):
     patient = Patient.query.get_or_404(patient_id)
-    form = EmptyForm() # Dùng để validate CSRF
+    if not current_user.is_admin:
+        flash('Permission denied.', 'danger')
+        return redirect(url_for('patients.index'))
 
+    form = EmptyForm() # Use for CSRF validation
     if form.validate_on_submit():
         try:
-            # Cần xem xét kỹ việc xóa cascade hoặc xóa thủ công các bản ghi liên quan
-            # Ví dụ: Xóa encounters, measurements, referrals, etc.
-            # Hiện tại chỉ xóa patient, CẦN ĐIỀU CHỈNH logic xóa liên quan nếu cần
-            
-            # Lấy tên bệnh nhân để hiển thị thông báo
-            patient_name = patient.full_name
+            patient_name = patient.full_name # Store name before deletion
+            patient_id_deleted = patient.id
             
-            # Xóa các bản ghi liên quan trước (VÍ DỤ - cần kiểm tra model relationships)
-            # Referral.query.filter_by(patient_id=patient.id).delete()
-            # Report.query.filter_by(patient_id=patient.id).delete()
-            # Procedure.query.filter_by(patient_id=patient.id).delete()
-            # PhysiologicalMeasurement.query.filter_by(patient_id=patient.id).delete()
+            # Consider cascading deletes or manual deletion of related records
+            # Example: Delete associated encounters, which might cascade further
             # Encounter.query.filter_by(patient_id=patient.id).delete()
-            # # Lưu ý: Thứ tự xóa quan trọng nếu có foreign key constraints
+            # Be VERY careful with cascading deletes or manual deletion order
             
             db.session.delete(patient)
+            
+            # Log activity
+            activity = ActivityLog(
+                user_id=current_user.userID,
+                action=f"Deleted patient: {patient_name} (ID: {patient_id_deleted})",
+                details=f"Patient ID: {patient_id_deleted}"
+            )
+            db.session.add(activity)
+
+            # *** Add Notification for Admins ***
+            create_notification_for_admins(
+                message=f"Patient {patient_name} (ID: {patient_id_deleted}) deleted by {current_user.full_name}.",
+                # No link as the patient is deleted
+                exclude_user_id=current_user.userID
+            )
+            # *** End Notification ***
+
             db.session.commit()
-            flash(f'Patient {patient_name} (ID: {patient_id}) has been deleted successfully.', 'success')
+            flash(f'Patient {patient_name} (ID: {patient_id_deleted}) has been deleted successfully.', 'success')
             return redirect(url_for('patients.index'))
         except Exception as e:
             db.session.rollback()
             current_app.logger.error(f"Error deleting patient {patient_id}: {str(e)}", exc_info=True)
             flash(f'Error deleting patient: {str(e)}', 'danger')
-            # Redirect về trang chi tiết nếu xóa lỗi
             return redirect(url_for('patients.patient_detail', patient_id=patient_id))
     else:
-        # Lỗi CSRF hoặc lỗi form khác
+        # CSRF error or other form error
         flash('Invalid request. Could not delete patient.', 'danger')
         return redirect(url_for('patients.index'))
 
 @patients_bp.route('/<string:patient_id>/assign_dietitian', methods=['POST'])
 @login_required
 def assign_dietitian(patient_id):
-    form = EmptyForm()
+    form = EmptyForm() # For CSRF validation
     if form.validate_on_submit():
-        patient = Patient.query.filter_by(id=patient_id).first_or_404()
+        patient = Patient.query.options(joinedload(Patient.referrals)).filter_by(id=patient_id).first_or_404()
+        
+        # --- Permission & Status Checks ---
+        if not current_user.is_admin:
+            flash('Permission denied.', 'danger')
+            return redirect(url_for('patients.patient_detail', patient_id=patient_id))
 
-        # Kiểm tra trạng thái bệnh nhân có phù hợp để gán không
         if patient.status != PatientStatus.NEEDS_ASSESSMENT:
-            flash(f'Bệnh nhân không ở trạng thái "{PatientStatus.NEEDS_ASSESSMENT.value}" để gán chuyên gia.', 'warning')
+            flash(f'Patient is not currently in {PatientStatus.NEEDS_ASSESSMENT.value} status.', 'warning')
             return redirect(url_for('patients.patient_detail', patient_id=patient_id))
+        # --- End Checks ---
 
-        assignment_type = request.form.get("assignment_type", "manual")
-        dietitian = None
+        assignment_type = request.form.get('assignment_type')
+        notes = request.form.get('notes', '')
+        selected_dietitian_id = None
+        assigned_dietitian = None
 
-        if assignment_type == "auto":
-            # Tự động gán cho dietitian có ít bệnh nhân nhất
-            # Query Users with Dietitian role and count their assigned patients
-            dietitians_with_counts = db.session.query(
-                User,
-                func.count(Patient.assigned_dietitian_user_id).label('patient_count')
+        try:
+            if assignment_type == 'auto':
+                # --- Auto Assign Logic --- 
+                current_app.logger.info(f"Attempting auto-assignment for patient {patient.id}")
+                # Find dietitian with the fewest assigned patients
+                # Query users with role Dietitian, join Patient on assigned_dietitian_user_id
+                # Count patients per dietitian, order by count ascending
+                dietitian_counts = db.session.query(
+                    User.userID,
+                    func.count(Patient.id).label('patient_count')
             ).outerjoin(Patient, User.userID == Patient.assigned_dietitian_user_id)\
              .filter(User.role == 'Dietitian')\
              .group_by(User.userID)\
-             .order_by(func.count(Patient.assigned_dietitian_user_id))\
+                 .order_by(func.count(Patient.id).asc())\
              .all()
 
-            if not dietitians_with_counts:
-                flash("Không tìm thấy chuyên gia dinh dưỡng nào trong hệ thống.", "error")
-                return redirect(url_for("patients.patient_detail", patient_id=patient_id))
-
-            # Chọn dietitian đầu tiên (có ít bệnh nhân nhất)
-            dietitian = dietitians_with_counts[0][0] # Lấy đối tượng User
-        else:
-            # Gán thủ công
-            dietitian_id = request.form.get("dietitian_id")
-            if not dietitian_id:
-                flash("Vui lòng chọn một chuyên gia dinh dưỡng.", "error")
-                return redirect(url_for("patients.patient_detail", patient_id=patient_id))
-
-            dietitian = User.query.get(dietitian_id)
-            if not dietitian or dietitian.role != 'Dietitian': # Kiểm tra cả role
-                flash("Chuyên gia dinh dưỡng không hợp lệ.", "error")
-                return redirect(url_for("patients.patient_detail", patient_id=patient_id))
-        
-        notes = request.form.get("notes", "")
-        
-        if patient.assigned_dietitian_user_id != dietitian.userID:
-            try:
-                # *** SỬA LOGIC TÌM ENCOUNTER/REFERRAL ***
-                # 1. Tìm referral mới nhất đang chờ gán cho bệnh nhân này
-                referral_to_update = Referral.query.filter_by(
-                    patient_id=patient.id,
-                    referral_status=ReferralStatus.DIETITIAN_UNASSIGNED
-                ).order_by(desc(Referral.referralRequestedDateTime)).first()
-
-                if not referral_to_update:
-                    flash(f'Không tìm thấy yêu cầu đánh giá (referral) nào đang chờ được gán cho bệnh nhân này.', 'error')
+                if not dietitian_counts:
+                    flash('No dietitians available for auto-assignment.', 'error')
                     return redirect(url_for('patients.patient_detail', patient_id=patient_id))
                 
-                # 2. Lấy encounter liên kết với referral đó
-                if not referral_to_update.encounter_id:
-                    flash(f'Referral đang chờ (ID: {referral_to_update.id}) không liên kết với lượt khám nào.', 'error')
-                    return redirect(url_for('patients.patient_detail', patient_id=patient_id))
-
-                encounter_to_update = Encounter.query.get(referral_to_update.encounter_id)
-
-                if not encounter_to_update:
-                    flash(f'Không tìm thấy lượt khám (encounter) liên kết với referral ID {referral_to_update.id}.', 'error')
-                    return redirect(url_for('patients.patient_detail', patient_id=patient_id))
+                # Select the dietitian with the lowest count
+                selected_dietitian_id = dietitian_counts[0][0] 
+                assigned_dietitian = User.query.get(selected_dietitian_id)
+                if not assigned_dietitian:
+                     # Should not happen if query is correct, but check anyway
+                     raise ValueError(f"Could not find dietitian user with ID {selected_dietitian_id} for auto-assignment.")
+                current_app.logger.info(f"Auto-assigning patient {patient.id} to dietitian {assigned_dietitian.full_name} (ID: {selected_dietitian_id}) with {dietitian_counts[0][1]} patients.")
+                # --- End Auto Assign Logic ---
+            
+            elif assignment_type == 'manual':
+                # --- Manual Assign Logic --- 
+                selected_dietitian_id = request.form.get('dietitian_id')
+                if not selected_dietitian_id:
+                    flash('Please select a dietitian for manual assignment.', 'warning')
+                    # Need to redirect back, ideally reopening the modal - harder without JS state saving
+                    # Simplest redirect for now:
+                    return redirect(url_for('patients.patient_detail', patient_id=patient_id, action='assign')) # Try to reopen modal
+                
+                assigned_dietitian = User.query.filter_by(userID=selected_dietitian_id, role='Dietitian').first()
+                if not assigned_dietitian:
+                    flash('Invalid dietitian selected.', 'error')
+                    return redirect(url_for('patients.patient_detail', patient_id=patient_id, action='assign'))
+                current_app.logger.info(f"Manually assigning patient {patient.id} to dietitian {assigned_dietitian.full_name} (ID: {selected_dietitian_id}). Notes: {notes}")
+                # --- End Manual Assign Logic ---
+            
+            else:
+                flash('Invalid assignment type specified.', 'error')
+                return redirect(url_for('patients.patient_detail', patient_id=patient_id)) # Sửa lỗi thụt lề
                 
-                # Optional: Kiểm tra trạng thái encounter có hợp lệ không (ví dụ: không phải FINISHED)
-                if encounter_to_update.status == EncounterStatus.FINISHED:
-                    flash(f'Lượt khám (ID: {encounter_to_update.custom_encounter_id or encounter_to_update.encounterID}) liên kết với referral đã hoàn thành.', 'warning')
-                    # Có thể vẫn cho phép gán nhưng logic sau đó cần xem xét?
-                    # Tạm thời cho phép tiếp tục, nhưng log lại
-                    current_app.logger.warning(f"Assigning dietitian to patient {patient_id} although associated encounter {encounter_to_update.encounterID} is FINISHED.")
-                # *** KẾT THÚC SỬA LOGIC TÌM KIẾM ***
-
-                # --- Bắt đầu cập nhật --- 
-                old_dietitian_id = patient.assigned_dietitian_user_id
-
-                # 1. Cập nhật Patient
-                patient.assigned_dietitian_user_id = dietitian.userID
+            # --- Update Patient and Related Objects --- 
+            if assigned_dietitian:
+                patient.assigned_dietitian_user_id = assigned_dietitian.userID
                 patient.assignment_date = datetime.utcnow()
-                patient.status = PatientStatus.ASSESSMENT_IN_PROGRESS
-
-                # 2. Cập nhật Encounter
-                encounter_to_update.status = EncounterStatus.ON_GOING
-                # Gán dietitian cho encounter luôn? (Có thể cần thiết nếu report/logic khác dựa vào đây)
-                encounter_to_update.dietitian_id = dietitian.userID 
-
-                # 3. Cập nhật Referral
-                referral_to_update.referral_status = ReferralStatus.WAITING_FOR_REPORT
-                referral_to_update.assigned_dietitian_user_id = dietitian.userID # Gán dietitian cho referral
+                patient.status = PatientStatus.ASSESSMENT_IN_PROGRESS # Update patient status
+                db.session.add(patient)
+                
+                # Find the latest relevant referral needing assignment and update it
+                latest_needing_referral = Referral.query.filter(
+                    Referral.patient_id == patient.id,
+                    Referral.referral_status == ReferralStatus.DIETITIAN_UNASSIGNED
+                ).order_by(desc(Referral.referralRequestedDateTime)).first()
 
-                # 4. Tạo Report mới
-                new_report = Report(
+                if latest_needing_referral:
+                    latest_needing_referral.assigned_dietitian_user_id = assigned_dietitian.userID
+                    latest_needing_referral.referral_status = ReferralStatus.WAITING_FOR_REPORT # Update referral status
+                    # Add assignment notes to referral notes? 
+                    if notes:
+                        latest_needing_referral.notes = f"(Assignment Note: {notes})\n{latest_needing_referral.notes or ''}"
+                    db.session.add(latest_needing_referral)
+                    current_app.logger.info(f"Updated referral {latest_needing_referral.id} status to WAITING_FOR_REPORT and assigned dietitian.")
+                else:
+                    current_app.logger.warning(f"Could not find a referral needing assignment for patient {patient.id} when assigning dietitian.")
+                
+                # --- ADD: Update latest ONGOING encounter with the assigned dietitian --- 
+                latest_ongoing_encounter = Encounter.query.filter_by(
                     patient_id=patient.id,
-                    encounter_id=encounter_to_update.encounterID,
-                    dietitian_id=dietitian.userID, # Lưu ID của dietitian tạo report
-                    author_id=current_user.userID, # Người tạo report là người đang phân công
-                    report_date=datetime.utcnow(),
-                    report_type='initial_assessment', # Thêm loại báo cáo
-                    status='Pending' # Trạng thái ban đầu của report
-                    # Thêm các trường mặc định khác nếu cần
-                )
-                db.session.add(new_report)
-                current_app.logger.info(f"Created new Report (ID: will be assigned on commit) for encounter {encounter_to_update.encounterID}")
-
-                # 5. Log hoạt động
-                activity_details = f"Patient ID: {patient.id}, Encounter ID: {encounter_to_update.encounterID}"
-                if notes:
-                    activity_details += f"\nAssignment Notes: {notes}"
-                action_message = f"Assigned dietitian {dietitian.full_name} to patient {patient.full_name}"
-                if assignment_type == "auto":
-                    action_message = f"AUTO-Assigned dietitian {dietitian.full_name} to patient {patient.full_name}"
+                    status=EncounterStatus.ON_GOING
+                ).order_by(desc(Encounter.start_time)).first()
                 
+                if latest_ongoing_encounter:
+                    latest_ongoing_encounter.dietitian_id = assigned_dietitian.userID
+                    db.session.add(latest_ongoing_encounter)
+                    # Đảm bảo refresh lại object để SQLAlchemy cập nhật relationship
+                    db.session.flush()
+                    # Nếu chạy mà vẫn không hiện tên dietitian, thử thêm dòng này:
+                    db.session.refresh(latest_ongoing_encounter)
+                    current_app.logger.info(f"Updated latest ongoing encounter {latest_ongoing_encounter.encounterID} with assigned dietitian {assigned_dietitian.userID}. Relationship loaded: {latest_ongoing_encounter.assigned_dietitian is not None}")
+                else:
+                    current_app.logger.warning(f"Could not find an ongoing encounter for patient {patient.id} to assign dietitian to.")
+                # --- END ADD ---
+                
+                # --- ADD: Update dietitian_id for related PENDING reports --- 
+                if latest_ongoing_encounter: # Chỉ cập nhật report nếu có encounter liên quan
+                    associated_pending_reports = Report.query.filter_by(
+                        encounter_id=latest_ongoing_encounter.encounterID,
+                        status=ReportStatus.PENDING
+                    ).all()
+                    
+                    updated_report_count = 0
+                    for report_to_update in associated_pending_reports:
+                        if report_to_update.dietitian_id is None: # Chỉ cập nhật nếu chưa có dietitian
+                            report_to_update.dietitian_id = assigned_dietitian.userID
+                            db.session.add(report_to_update)
+                            updated_report_count += 1
+                            
+                    if updated_report_count > 0:
+                        current_app.logger.info(f"Updated dietitian_id for {updated_report_count} pending reports associated with encounter {latest_ongoing_encounter.encounterID}.")
+                # --- END ADD --- 
+
+                # --- Logging and Notifications --- 
+                # Log Activity
+                log_message = f"Assigned dietitian {assigned_dietitian.full_name} to patient {patient.full_name} ({assignment_type} assignment)."
                 activity = ActivityLog(
                     user_id=current_user.userID,
-                    action=action_message,
-                    details=activity_details
+                    action=log_message,
+                    details=f"Patient ID: {patient.id}, Dietitian ID: {assigned_dietitian.userID}, Notes: {notes}"
                 )
                 db.session.add(activity)
 
-                # 6. Thông báo cho dietitian mới
-                notification = Notification(
-                    user_id=dietitian.userID,
-                    message=f"Bạn đã được gán cho bệnh nhân {patient.full_name} (ID: {patient.id}) cho lượt khám {encounter_to_update.custom_encounter_id or encounter_to_update.encounterID}. Vui lòng hoàn thành báo cáo đánh giá.",
-                    is_read=False,
-                    link=url_for("patients.patient_detail", patient_id=patient.id) # Link đến patient detail hoặc trang report?
+                # Create Notification for the assigned Dietitian
+                create_notification(
+                    recipient_user_id=assigned_dietitian.userID,
+                    message=f"You have been assigned to patient {patient.full_name}.",
+                    link=('patients.patient_detail', {'patient_id': patient.id})
+                )
+                # Optionally notify other admins (excluding current user)
+                create_notification_for_admins(
+                     message=f"Dietitian {assigned_dietitian.full_name} assigned to patient {patient.full_name} by {current_user.full_name}.",
+                     link=('patients.patient_detail', {'patient_id': patient.id}),
+                     exclude_user_id=current_user.userID
                 )
-                db.session.add(notification)
+                # --- End Logging and Notifications ---
 
-                # 7. Commit tất cả thay đổi
                 db.session.commit()
+                flash(f'Successfully assigned dietitian {assigned_dietitian.full_name} to {patient.full_name}.', 'success')
+            else: 
+                # This case should ideally not be reached if logic above is correct
+                flash('Could not determine dietitian to assign.', 'error')
+
+        except Exception as e: # Sửa lỗi thụt lề
+            db.session.rollback() # Sửa lỗi thụt lề
+            current_app.logger.error(f"Error assigning dietitian for patient {patient.id}: {str(e)}", exc_info=True) # Sửa lỗi thụt lề
+            flash(f'An error occurred during assignment: {str(e)}', 'danger') # Sửa lỗi thụt lề
+
+        # --- RETURN STATEMENT --- 
+        # Always redirect back to the patient detail page after processing
+        return redirect(url_for('patients.patient_detail', patient_id=patient_id))
+
+        # Handle CSRF error or non-POST request (though route only accepts POST)
+        flash('Invalid request.', 'danger')
+        # Redirect back to patient detail or maybe index?
+        return redirect(url_for('patients.patient_detail', patient_id=patient_id if patient_id else 'index')) # Need a fallback if patient_id isn't available
+
+# --- NEW ROUTE FOR BULK AUTO ASSIGN --- 
+@patients_bp.route('/bulk_auto_assign', methods=['POST'])
+@login_required
+def bulk_auto_assign():
+    """Handles the bulk auto-assignment of dietitians to selected patients."""
+    if not current_user.is_admin:
+        flash('You do not have permission to perform this action.', 'error')
+        return redirect(url_for('patients.index'))
 
-                flash_message = f"Đã gán bệnh nhân cho chuyên gia {dietitian.full_name}. Trạng thái cập nhật thành '{PatientStatus.ASSESSMENT_IN_PROGRESS.value}'. Báo cáo mới đã được tạo."
-                if assignment_type == "auto":
-                     flash_message = f"Đã TỰ ĐỘNG gán bệnh nhân cho chuyên gia {dietitian.full_name}. Trạng thái cập nhật thành '{PatientStatus.ASSESSMENT_IN_PROGRESS.value}'. Báo cáo mới đã được tạo."
-                flash(flash_message, "success")
+    # Get the list of patient IDs from the form
+    selected_patient_ids = request.form.getlist('selected_patients')
 
-            except Exception as e:
-                db.session.rollback()
-                current_app.logger.error(f"Lỗi khi gán dietitian và cập nhật trạng thái: {str(e)}", exc_info=True)
-                flash(f"Đã xảy ra lỗi khi gán chuyên gia dinh dưỡng: {str(e)}", "error")
-        else:
-            flash(f"Bệnh nhân đã được gán cho chuyên gia {dietitian.full_name} rồi.", "info")
+    if not selected_patient_ids:
+        flash('No patients selected for assignment.', 'warning')
+        return redirect(url_for('patients.index'))
+
+    current_app.logger.info(f"Bulk auto-assign requested by admin {current_user.userID} for patients: {selected_patient_ids}")
+
+    # --- PLACEHOLDER LOGIC ---
+    # TODO: Implement the actual logic for finding the least busy dietitian
+    #       and assigning them to the selected patients.
+    #       This might involve:
+    #       1. Querying dietitians and their current patient counts.
+    #       2. Finding the dietitian with the minimum count.
+    #       3. Iterating through selected_patient_ids:
+    #          - Get the Patient object.
+    #          - Check if the patient needs assignment (e.g., status is NEEDS_ASSESSMENT).
+    #          - Update patient.assigned_dietitian_user_id.
+    #          - Update patient.status.
+    #          - Create notifications/activity logs.
+    #          - Commit changes.
+    #       4. Handle potential errors (e.g., no dietitians available).
+
+    assigned_count = 0
+    errors = []
+
+    # Example placeholder: Just log and flash success for now
+    assigned_count = len(selected_patient_ids) # Simulate assignment
+    
+    # --- END PLACEHOLDER ---
 
-        return redirect(url_for("patients.patient_detail", patient_id=patient_id))
+    if assigned_count > 0:
+        flash(f'Successfully initiated auto-assignment for {assigned_count} patient(s).', 'success')
+        # You might want to create a single notification for the admin summarizing the action
+        # create_notification(current_user.userID, f"Bulk auto-assigned {assigned_count} patients.")
+    if errors:
+        flash(f'Could not assign all selected patients. Errors: {"; ".join(errors)}', 'danger')
 
-    # Nếu form không validate (ví dụ CSRF lỗi) - Đảm bảo thụt lề đúng ở đây
-    flash('Yêu cầu không hợp lệ.', 'danger')
-    patient_id_from_url = request.view_args.get('patient_id') # Lấy patient_id từ URL
-    if patient_id_from_url:
-        return redirect(url_for("patients.patient_detail", patient_id=patient_id_from_url))
-    else:
-        return redirect(url_for("main.handle_root")) # Fallback về trang chủ nếu không lấy được patient_id
+    return redirect(url_for('patients.index'))
+# --- END NEW ROUTE ---
diff --git a/app/routes/report.py b/app/routes/report.py
index 69d0fc6ba92cbe6874520bc8e287453a156b6fd7..a621d021a003d2a82d45a28d1e5e1509a60d69e5 100644
--- a/app/routes/report.py
+++ b/app/routes/report.py
@@ -305,18 +305,31 @@ def edit_report(report_id):
     # 1. Admin can always edit
     if current_user.is_admin:
         can_edit = True
-    # 2. Author can always edit
-    elif report.author_id == current_user.userID:
-        can_edit = True
-    # 3. Assigned dietitian for the PATIENT can edit Draft or Pending reports
-    elif patient and patient.assigned_dietitian_user_id == current_user.userID and report.status in ['Draft', 'Pending']:
-        can_edit = True
+    # 2. Dietitian can edit if it's assigned to them AND status is Draft/Pending
+    # Check if dietitian is assigned directly to the report FIRST
+    # Also check status using .value for safety
+    elif current_user.role.upper() == 'DIETITIAN' and report.dietitian_id == current_user.userID and (report.status.value if report.status else None) in ['Draft', 'Pending']:
+         can_edit = True
+    # 3. NEW: Điều kiện thứ ba - Kiểm tra xem dietitian có phải là người được gán cho bệnh nhân không
+    elif current_user.role.upper() == 'DIETITIAN' and patient and patient.assigned_dietitian_user_id == current_user.userID and (report.status.value if report.status else None) in ['Draft', 'Pending']:
+         can_edit = True
+    # 4. Fallback: Check if the user is the ORIGINAL AUTHOR and status is Draft/Pending (e.g., if dietitian hasn't been formally assigned yet)
+    #    This might be less common if assignment happens early.
+    # Also check status using .value for safety
+    elif report.author_id == current_user.userID and (report.status.value if report.status else None) in ['Draft', 'Pending']:
+         can_edit = True # Original author can edit drafts/pending
 
     if not can_edit:
         flash('You do not have permission to edit this report.', 'error')
         return redirect(url_for('report.view_report', report_id=report.id))
     # --- End Permission Check ---
 
+    # Check if report is editable (Draft or Pending) - redundant now with above check, but keep for clarity maybe?
+    # Also check status using .value for safety
+    if (report.status.value if report.status else None) == 'Completed':
+         flash('Cannot edit a completed report.', 'warning')
+         return redirect(url_for('report.view_report', report_id=report.id))
+
     # Truyền patient_id vào form để load procedure choices
     # Khởi tạo form với obj=report để điền dữ liệu hiện có
     form = ReportForm(patient_id=report.patient_id, obj=report)
@@ -363,54 +376,133 @@ def edit_report(report_id):
         if action == 'save_draft':
             report.status = 'Draft'
             print(f"--- Action: Save Draft detected. OVERRIDING status to Draft. ---")
-        elif action == 'complete': # Xử lý action complete ở đây
-            # Lưu trước báo cáo
-            report.updated_at = datetime.utcnow()
+            report.updated_at = datetime.utcnow() # Update timestamp for draft save too
+            # Commit draft save separately before redirecting
             try:
                 db.session.commit()
-                print(f"--- Action: Complete detected. Saved report and redirecting to complete_report route... ---")
-                # Chuyển hướng đến complete_report route với báo cáo đã lưu
-                return redirect(url_for('report.complete_report', report_id=report.id))
+                flash('Report draft saved successfully.', 'success')
+                # Redirect to view report after saving draft
+                return redirect(url_for('report.view_report', report_id=report.id))
             except Exception as e:
                 db.session.rollback()
-                current_app.logger.error(f"Database error updating report before complete {report_id}: {e}", exc_info=True)
-                flash(f'Lỗi khi lưu báo cáo trước khi hoàn thành: {e}', 'error')
-                # Reload choices và render lại form khi gặp lỗi DB
-                form.patient_id.choices = [(p.id, f"{p.full_name} ({p.id})") for p in Patient.query.order_by(Patient.lastName, Patient.firstName).all()]
-                form.patient_id.choices.insert(0, ('', '-- Select Patient --'))
+                current_app.logger.error(f"Database error saving report draft {report_id}: {e}", exc_info=True)
+                flash(f'Database error occurred while saving draft: {e}', 'error')
+                # Find first error for scrolling on DB error
                 if form.errors:
                     first_error_field = next(iter(form.errors))
                     first_error_field_id = form[first_error_field].id if first_error_field in form else None
-                return render_template('report_form.html', form=form, report=report, edit_mode=True, attachments=[], first_error_field_id=first_error_field_id)
-        else:
-             print(f"--- Action: {action}. Using status from populate_obj: {report.status} ---")
+                # Reload choices before rendering again after DB error
+                form.patient_id.choices = [(p.id, f"{p.full_name} ({p.id})") for p in Patient.query.order_by(Patient.lastName, Patient.firstName).all()]
+                form.patient_id.choices.insert(0, ('', '-- Select Patient --'))
+                # Load procedures again
+                patient_procedures = Procedure.query.filter_by(patient_id=report.patient_id).order_by(Procedure.procedureDateTime.desc()).all()
+                form.related_procedure_id.choices = [('', '-- Select Procedure (Optional) --')] + \
+                                                    [(proc.id, f"#{proc.id} - {proc.procedureType} ({proc.procedureDateTime.strftime('%d/%m/%y %H:%M') if proc.procedureDateTime else 'N/A'})") 
+                                                     for proc in patient_procedures]
+                # Add EncounterStatus here
+                return render_template('report_form.html', form=form, report=report, edit_mode=True, attachments=[], first_error_field_id=first_error_field_id, EncounterStatus=EncounterStatus)
+
+        elif action == 'complete': # Handle completion directly here
+            print(f"--- Action: Complete detected. Processing completion logic... ---")
+            # Logic moved from complete_report route
+            try:
+                # Permission check is already done before form validation
+                # Status check (Draft/Pending) is implicitly handled by permission check logic
+
+                # Find related objects (patient already loaded)
+                patient = report.patient
+                encounter = Encounter.query.get(report.encounter_id) if report.encounter_id else None
+                referral = Referral.query.filter_by(encounter_id=report.encounter_id).first() if report.encounter_id else None
+
+                if not patient:
+                    flash('Cannot find related Patient.', 'error')
+                    return redirect(url_for('report.index'))
+
+                # --- Update statuses ---
+                # 1. Report: Set status directly to Completed
+                report.status = 'Completed'
+                # report.completed_date = datetime.utcnow() # Uncomment if column exists
+                report.updated_at = datetime.utcnow()
+
+                # *** ADD CHECK FOR EMPTY encounter_id ***
+                # Ensure encounter_id is None if it's an empty string after populate_obj
+                if report.encounter_id == '':
+                    current_app.logger.warning(f"Report {report.id} had empty string for encounter_id after populate_obj. Setting to None before commit.")
+                    report.encounter_id = None 
+                # *** END CHECK ***
+
+                # 2. Patient
+                patient.status = PatientStatus.COMPLETED
+
+                # 3. Encounter
+                # Check if encounter_id exists before trying to get encounter
+                if report.encounter_id:
+                    encounter = Encounter.query.get(report.encounter_id)
+                    if encounter:
+                        encounter.status = EncounterStatus.FINISHED
+                        encounter.end_time = datetime.utcnow()
+                    else:
+                         current_app.logger.warning(f"Could not find encounter with ID {report.encounter_id} when completing report {report.id}")
+                else:
+                    current_app.logger.info(f"Report {report.id} does not have an associated encounter_id.")
+
+                # 4. Referral
+                # Check if encounter_id exists before trying to get referral
+                if report.encounter_id:
+                    referral = Referral.query.filter_by(encounter_id=report.encounter_id).first()
+                    if referral:
+                        referral.referral_status = ReferralStatus.COMPLETED
+                        # referral.referralCompletedDateTime = datetime.utcnow() # Uncomment if column exists
+                    elif report.encounter_id: # Check encounter_id again for clarity in log
+                        current_app.logger.warning(f"No matching Referral found for encounter {report.encounter_id} when completing report {report.id}")
+                else:
+                     current_app.logger.info(f"Report {report.id} does not have an associated encounter_id to find referral.")
+
+                # 5. Commit changes
+                db.session.commit()
+                flash('Report completed successfully. Associated statuses updated.', 'success')
+                current_app.logger.info(f"Report {report.id} completed by User {current_user.userID} within edit_report route.")
 
-        report.updated_at = datetime.utcnow()
+                # Redirect to patient detail page after completion
+                return redirect(url_for('patients.patient_detail', patient_id=report.patient_id, _anchor='reports'))
 
-        try:
-            # Debugging: Log before commit
-            print(f"--- Committing report update. ID: {report.id}, Final Status: {report.status}, Summary: {report.intervention_summary[:50] if report.intervention_summary else 'None'}... ---")
-            db.session.commit()
-            # TODO: Xử lý file attachments mới và xóa file cũ nếu cần
-            flash('Report updated successfully.', 'success')
-            # Redirect to view report after successful save/update
-            return redirect(url_for('report.view_report', report_id=report.id))
-        except Exception as e:
-            db.session.rollback()
-            current_app.logger.error(f"Database error updating report {report_id}: {e}", exc_info=True)
-            flash(f'Database error occurred while updating report: {e}', 'error')
-            # Find first error for scrolling on DB error
-            if form.errors: # Check form errors again? Might not be relevant here.
-                 first_error_field = next(iter(form.errors))
-                 first_error_field_id = form[first_error_field].id if first_error_field in form else None
-            # Reload choices before rendering again after DB error
-            form.patient_id.choices = [(p.id, f"{p.full_name} ({p.id})") for p in Patient.query.order_by(Patient.lastName, Patient.firstName).all()]
-            form.patient_id.choices.insert(0, ('', '-- Select Patient --'))
-            patient_procedures = Procedure.query.filter_by(patient_id=report.patient_id).order_by(Procedure.procedureDateTime.desc()).all()
-            form.related_procedure_id.choices = [('', '-- Select Procedure (Optional) --')] + \
-                                                [(proc.id, f"#{proc.id} - {proc.procedureType} ({proc.procedureDateTime.strftime('%d/%m/%y %H:%M') if proc.procedureDateTime else 'N/A'})") 
-                                                 for proc in patient_procedures]
-            return render_template('report_form.html', form=form, report=report, edit_mode=True, attachments=[], first_error_field_id=first_error_field_id) # Pass attachments
+            except Exception as e:
+                db.session.rollback()
+                current_app.logger.error(f"Error completing report {report_id} within edit_report: {str(e)}", exc_info=True)
+                flash(f'An error occurred while completing the report: {str(e)}', 'danger')
+                # Render edit form again on error
+                form.patient_id.choices = [(p.id, f"{p.full_name} ({p.id})") for p in Patient.query.order_by(Patient.lastName, Patient.firstName).all()]
+                form.patient_id.choices.insert(0, ('', '-- Select Patient --'))
+                patient_procedures = Procedure.query.filter_by(patient_id=report.patient_id).order_by(Procedure.procedureDateTime.desc()).all()
+                form.related_procedure_id.choices = [('', '-- Select Procedure (Optional) --')] + \
+                                                    [(proc.id, f"#{proc.id} - {proc.procedureType} ({proc.procedureDateTime.strftime('%d/%m/%y %H:%M') if proc.procedureDateTime else 'N/A'})") 
+                                                     for proc in patient_procedures]
+                # Add EncounterStatus here
+                return render_template('report_form.html', form=form, report=report, edit_mode=True, attachments=[], first_error_field_id=None, EncounterStatus=EncounterStatus)
+        
+        else: # Handle default save/update if no specific action or action='save'
+             # This part might need review - currently only 'save_draft' and 'complete' explicitly commit.
+             # Should there be a generic 'save' action that commits updates without changing status from Draft/Pending?
+             print(f"--- Action: {action}. No explicit commit logic for this action in edit_report. Using status from populate_obj: {report.status} ---")
+             # Perhaps add a commit here as well?
+             report.updated_at = datetime.utcnow()
+             try:
+                 db.session.commit()
+                 flash('Report updated successfully.', 'success')
+                 return redirect(url_for('report.view_report', report_id=report.id))
+             except Exception as e:
+                 db.session.rollback()
+                 current_app.logger.error(f"Database error updating report {report_id} (default action): {e}", exc_info=True)
+                 flash(f'Database error occurred while updating report: {e}', 'error')
+                 # Reload choices and render form
+                 form.patient_id.choices = [(p.id, f"{p.full_name} ({p.id})") for p in Patient.query.order_by(Patient.lastName, Patient.firstName).all()]
+                 form.patient_id.choices.insert(0, ('', '-- Select Patient --'))
+                 patient_procedures = Procedure.query.filter_by(patient_id=report.patient_id).order_by(Procedure.procedureDateTime.desc()).all()
+                 form.related_procedure_id.choices = [('', '-- Select Procedure (Optional) --')] + \
+                                                     [(proc.id, f"#{proc.id} - {proc.procedureType} ({proc.procedureDateTime.strftime('%d/%m/%y %H:%M') if proc.procedureDateTime else 'N/A'})") 
+                                                      for proc in patient_procedures]
+                 # Add EncounterStatus here
+                 return render_template('report_form.html', form=form, report=report, edit_mode=True, attachments=[], first_error_field_id=None, EncounterStatus=EncounterStatus)
 
     elif request.method == 'POST': # Validation failed on POST
         current_app.logger.warning(f"Report form validation failed. Errors: {form.errors}")
@@ -420,11 +512,20 @@ def edit_report(report_id):
             first_error_field_id = form[first_error_field].id if first_error_field in form else None # Get ID from form field object
             print(f"--- First error field ID for scroll: {first_error_field_id} ---")
 
-        # ... (existing code to reload choices) ...
+        # Reload choices and render form
+        form.patient_id.choices = [(p.id, f"{p.full_name} ({p.id})") for p in Patient.query.order_by(Patient.lastName, Patient.firstName).all()]
+        form.patient_id.choices.insert(0, ('', '-- Select Patient --'))
+        patient_procedures = Procedure.query.filter_by(patient_id=report.patient_id).order_by(Procedure.procedureDateTime.desc()).all()
+        form.related_procedure_id.choices = [('', '-- Select Procedure (Optional) --')] + \
+                                            [(proc.id, f"#{proc.id} - {proc.procedureType} ({proc.procedureDateTime.strftime('%d/%m/%y %H:%M') if proc.procedureDateTime else 'N/A'})") 
+                                             for proc in patient_procedures]
+        # Add EncounterStatus here too
+        return render_template('report_form.html', form=form, report=report, edit_mode=True, attachments=[], first_error_field_id=first_error_field_id, EncounterStatus=EncounterStatus)
 
     # Render form chỉnh sửa (GET request or POST validation failed)
     attachments = [] # TODO: Lấy danh sách attachments nếu có
     # Pass the ID of the first error field to the template
+    # Add EncounterStatus here
     return render_template(
         'report_form.html', 
         form=form, 
@@ -838,71 +939,6 @@ def create_stats_report(report_type):
             mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
         )
 
-# Route mới để hoàn thành Report và cập nhật trạng thái liên quan
-@report_bp.route('/<int:report_id>/complete', methods=['POST', 'GET'])
-@login_required
-def complete_report(report_id):
-    """Hoàn thành report, cập nhật trạng thái Patient, Encounter, Referral."""
-    report = Report.query.get_or_404(report_id)
-
-    # Kiểm tra quyền: Chỉ dietitian được gán cho report hoặc admin
-    if not current_user.is_admin and (
-        current_user.userID != report.author_id and 
-        (report.patient and current_user.userID != report.patient.assigned_dietitian_user_id)):
-        flash('Bạn không có quyền hoàn thành báo cáo này.', 'error')
-        return redirect(url_for('report.view_report', report_id=report_id))
-
-    # Kiểm tra trạng thái report có phải là 'Pending' hoặc trạng thái phù hợp không?
-    if report.status not in ['Draft', 'Pending']: # Cho phép hoàn thành từ Draft hoặc Pending
-        flash(f'Báo cáo này không ở trạng thái "Draft" hoặc "Pending" để hoàn thành (hiện tại: {report.status}).', 'warning')
-        return redirect(url_for('report.view_report', report_id=report_id))
-
-    try:
-        # Tìm các đối tượng liên quan
-        patient = Patient.query.get(report.patient_id)
-        encounter = Encounter.query.get(report.encounter_id) if report.encounter_id else None
-        # Tìm referral tương ứng (dựa trên encounter_id)
-        referral = Referral.query.filter_by(encounter_id=report.encounter_id).first() if report.encounter_id else None
-
-        if not patient:
-            flash('Không tìm thấy Bệnh nhân liên quan.', 'error')
-            return redirect(url_for('report.view_report', report_id=report_id))
-
-        # --- Bắt đầu cập nhật trạng thái --- 
-        # 1. Cập nhật Report
-        report.status = 'Completed' # Hoặc 'Finalized' tùy theo Enum của bạn
-        report.completed_date = datetime.utcnow() # Thêm cột completed_date vào Report model
-
-        # 2. Cập nhật Patient
-        patient.status = PatientStatus.COMPLETED
-
-        # 3. Cập nhật Encounter (nếu có)
-        if encounter:
-            encounter.status = EncounterStatus.FINISHED
-            encounter.end_time = datetime.utcnow() # Đặt thời gian kết thúc encounter
-
-        # 4. Cập nhật Referral (nếu tìm thấy)
-        if referral:
-            referral.referral_status = ReferralStatus.COMPLETED
-            referral.referralCompletedDateTime = datetime.utcnow()
-        else:
-            if report.encounter_id:
-                current_app.logger.warning(f"Không tìm thấy Referral tương ứng cho encounter {report.encounter_id} khi hoàn thành report {report.id}")
-
-        # 5. Commit thay đổi
-        db.session.commit()
-        flash('Báo cáo đã được hoàn thành thành công. Trạng thái Bệnh nhân, Lượt khám và Yêu cầu đánh giá đã được cập nhật.', 'success')
-        current_app.logger.info(f"Report {report.id} completed by User {current_user.userID}. Associated Patient/Encounter/Referral statuses updated.")
-
-        # Redirect về trang chi tiết bệnh nhân
-        return redirect(url_for('patients.patient_detail', patient_id=report.patient_id, _anchor='reports'))
-
-    except Exception as e:
-        db.session.rollback()
-        current_app.logger.error(f"Lỗi khi hoàn thành report {report_id}: {str(e)}", exc_info=True)
-        flash(f'Đã xảy ra lỗi khi hoàn thành báo cáo: {str(e)}', 'danger')
-        return redirect(url_for('report.view_report', report_id=report_id))
-
 @report_bp.route('/<int:report_id>/delete', methods=['POST'])
 @login_required
 def delete_report(report_id):
diff --git a/app/routes/support.py b/app/routes/support.py
index 31f8d9174aaeb6445e15d8d2879184dd55b86561..583622b2b8cade6325cc5d220ab11151f4df3e1c 100644
--- a/app/routes/support.py
+++ b/app/routes/support.py
@@ -1,10 +1,10 @@
 from flask import Blueprint, render_template, request, flash, redirect, url_for, jsonify, current_app
 from flask_login import login_required, current_user
 from .. import db
-from ..models import SupportMessage, SupportMessageReadStatus, User
-from ..forms import SupportMessageForm # Sẽ tạo form này sau
-from sqlalchemy import desc, or_, func # Import func
-from datetime import datetime
+from ..models import SupportMessage, User
+from ..forms import SupportMessageForm
+from sqlalchemy import desc
+from datetime import datetime, timezone
 
 support_bp = Blueprint('support', __name__, url_prefix='/support')
 
@@ -16,8 +16,6 @@ def index():
         flash('Bạn không có quyền truy cập trang này.', 'danger')
         return redirect(url_for('main.handle_root'))
 
-    # Sẽ tạo SupportMessageForm trong app/forms/__init__.py hoặc file riêng
-    # Tạm thời giả định nó đã tồn tại và có trường 'content'
     form = SupportMessageForm()
 
     if form.validate_on_submit():
@@ -25,7 +23,7 @@ def index():
             new_message = SupportMessage(
                 sender_id=current_user.userID,
                 content=form.content.data,
-                timestamp=datetime.utcnow() # Đảm bảo timestamp được set
+                timestamp=datetime.utcnow()
             )
             db.session.add(new_message)
             db.session.commit()
@@ -41,53 +39,38 @@ def index():
         db.joinedload(SupportMessage.sender) # Tải sẵn thông tin người gửi
     ).order_by(desc(SupportMessage.timestamp)).all()
 
-    # Đánh dấu các tin nhắn là đã đọc cho user hiện tại
-    unread_message_ids = db.session.query(SupportMessage.id).\
-        outerjoin(SupportMessageReadStatus, 
-                  (SupportMessage.id == SupportMessageReadStatus.message_id) & 
-                  (SupportMessageReadStatus.user_id == current_user.userID)).\
-        filter(SupportMessageReadStatus.id == None).\
-        filter(SupportMessage.sender_id != current_user.userID).\
-        all()
-        
-    unread_message_ids = [m[0] for m in unread_message_ids] 
-
-    if unread_message_ids:
-        now = datetime.utcnow()
-        new_read_statuses = []
-        for msg_id in unread_message_ids:
-            read_status = SupportMessageReadStatus(
-                user_id=current_user.userID, 
-                message_id=msg_id,
-                read_at=now
-            )
-            new_read_statuses.append(read_status)
-        
-        if new_read_statuses:
-            try:
-                db.session.add_all(new_read_statuses)
-                db.session.commit()
-                current_app.logger.info(f"User {current_user.userID} marked {len(new_read_statuses)} support messages as read.")
-            except Exception as e:
-                db.session.rollback()
-                current_app.logger.error(f"Lỗi khi đánh dấu tin nhắn support đã đọc cho user {current_user.userID}: {e}")
+    # Cập nhật thời gian truy cập trang support lần cuối
+    try:
+        current_user.last_support_visit = datetime.now(timezone.utc)
+        db.session.commit()
+    except Exception as e:
+        db.session.rollback()
+        current_app.logger.error(f"Lỗi khi cập nhật last_support_visit cho user {current_user.userID}: {e}")
+        # Không cần flash lỗi cho người dùng về việc này
 
     return render_template('support.html', messages=messages, form=form)
 
-
 @support_bp.route('/unread_count')
 @login_required
 def unread_count():
-    """API endpoint trả về số lượng tin nhắn support chưa đọc."""
+    """API endpoint trả về số lượng tin nhắn support mới (chưa xem)."""
     if current_user.role not in ['Admin', 'Dietitian']:
         return jsonify(count=0)
 
-    count = db.session.query(func.count(SupportMessage.id)).\
-        outerjoin(SupportMessageReadStatus, 
-                  (SupportMessage.id == SupportMessageReadStatus.message_id) & 
-                  (SupportMessageReadStatus.user_id == current_user.userID)).\
-        filter(SupportMessageReadStatus.id == None).\
-        filter(SupportMessage.sender_id != current_user.userID).\
-        scalar()
-        
-    return jsonify(count=count or 0) 
\ No newline at end of file
+    last_visit = current_user.last_support_visit
+    
+    query = SupportMessage.query.filter(
+        SupportMessage.sender_id != current_user.userID # Chỉ đếm tin nhắn từ người khác
+    )
+    
+    if last_visit:
+        # Đảm bảo last_visit là timezone-aware (UTC) nếu timestamp là timezone-aware
+        # Hoặc chuyển cả hai về naive nếu cần
+        # Giả sử cả hai đều là UTC timezone-aware hoặc naive
+        query = query.filter(SupportMessage.timestamp > last_visit)
+    
+    count = query.count()
+            
+    return jsonify(count=count or 0)
+
+# Removed: /unread_count endpoint 
\ No newline at end of file
diff --git a/app/routes/upload.py b/app/routes/upload.py
index cef5008a43e199d83ec9ff61c1ad823a64c7af30..083951b9e4032451964386f83d6a946a812f7279 100644
--- a/app/routes/upload.py
+++ b/app/routes/upload.py
@@ -4,7 +4,7 @@ import io
 import json
 import uuid
 from datetime import datetime
-from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, send_from_directory, session
+from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, send_from_directory, session, Response
 from flask_login import login_required, current_user
 from werkzeug.utils import secure_filename
 from werkzeug.datastructures import FileStorage
@@ -101,59 +101,53 @@ def index():
                 db.session.add(upload_record)
                 db.session.commit()
                 
-                # Xử lý file CSV
+                # Xử lý file CSV (Hàm này cần được sửa để trả về cấu trúc mới)
                 result = process_new_patients_csv(
                     uploaded_file_id=upload_record.id
                 )
                 
-                # Cập nhật trạng thái
-                upload_record.status = 'completed' if result.get('success') else 'failed'
-                if result.get('success'):
-                    upload_record.processed_records = result.get('processed_records', 0)
-                    upload_record.error_records = result.get('error_records', 0)
-                    upload_record.total_records = result.get('total_records', 0)
-                    if result.get('errors'):
-                        upload_record.error_details = json.dumps(result['errors'])
-                else:
-                    upload_record.error_details = json.dumps([{'row': 0, 'error': result.get('error', 'Unknown error')}])
-                
-                db.session.commit()
-                
+                # Cập nhật trạng thái dựa trên kết quả mới
+                upload_record.processed_records = result.get('processed_records', 0)
+                upload_record.error_records = result.get('error_records', 0)
+                upload_record.total_records = result.get('total_records', 0)
+                upload_record.error_details = json.dumps(result.get('error_details', {})) # Lưu chi tiết lỗi (nếu có)
+
+                duplicate_errors = result.get('duplicate_errors', [])
+                format_errors_exist = result.get('format_errors', False)
+                num_duplicates = len(duplicate_errors)
+
                 if result.get('success'):
-                    processed_count = result.get('processed_records', 0)
-                    error_count = result.get('error_records', 0)
-                    if error_count > 0:
-                        # Hiển thị thông báo lỗi dễ hiểu cho người dùng
-                        error_details = result.get('error_details', '{}')
-                        try:
-                            error_json = json.loads(error_details)
-                            if 'summary' in error_json:
-                                # Đây là lỗi đã được tóm tắt
-                                flash(f"{error_json.get('summary')} {error_json.get('message', '')}", 'warning')
-                            else:
-                                flash(f'Đã tải lên và xử lý {processed_count} bản ghi, nhưng có {error_count} lỗi. Xem chi tiết trong lịch sử tải lên.', 'warning')
-                        except:
-                            flash(f'Đã tải lên và xử lý {processed_count} bản ghi, nhưng có {error_count} lỗi. Xem chi tiết trong lịch sử tải lên.', 'warning')
+                    # Thành công hoàn toàn hoặc có lỗi trùng lặp
+                    if num_duplicates > 0:
                         upload_record.status = 'completed_with_errors'
+                        if num_duplicates > 3:
+                            flash(f'Đã xử lý {upload_record.processed_records} bản ghi. Phát hiện trùng lặp {num_duplicates} bệnh nhân.', 'warning')
+                        else:
+                            colliding_names = ", ".join(duplicate_errors)
+                            flash(f'Đã xử lý {upload_record.processed_records} bản ghi. Bệnh nhân trùng lặp: {colliding_names}.', 'warning')
                     else:
-                        flash(f'Đã tải lên và xử lý thành công {processed_count} bản ghi từ {filename}.', 'success')
                         upload_record.status = 'completed'
+                        flash(f'Đã tải lên và xử lý thành công {upload_record.processed_records} bản ghi từ {filename}.', 'success')
                 else:
-                    error_msg = result.get('error', 'Unknown processing error')
-                    # Đơn giản hóa thông báo lỗi cho người dùng cuối
-                    if 'Data too long' in error_msg:
-                        user_error_message = "Dữ liệu lỗi quá lớn để lưu chi tiết. Vui lòng kiểm tra tóm tắt lỗi trong lịch sử tải lên."
-                        # Ghi log lỗi đầy đủ cho dev
-                        current_app.logger.error(f"Data too long error during CSV processing for file ID {upload_record.id}: {error_msg}")
-                    elif 'already exists' in error_msg: # Có thể bắt thêm các lỗi cụ thể khác
-                        user_error_message = "Phát hiện dữ liệu bệnh nhân trùng lặp. Xem chi tiết trong lịch sử tải lên."
-                    else:
-                        user_error_message = f"Xử lý file thất bại. Vui lòng kiểm tra định dạng file hoặc xem chi tiết lỗi trong lịch sử tải lên."
-                        # Ghi log lỗi không xác định
-                        current_app.logger.error(f"Unknown error during CSV processing for file ID {upload_record.id}: {error_msg}")
-
-                    flash(user_error_message, 'error')
+                    # Có lỗi format hoặc lỗi nghiêm trọng khác
                     upload_record.status = 'failed'
+                    # Ưu tiên hiển thị lỗi format nếu có
+                    if format_errors_exist:
+                        flash('Lỗi định dạng file CSV không đúng. Vui lòng kiểm tra lại cấu trúc cột hoặc tải xuống file mẫu.', 'error')
+                    else:
+                        # Xử lý lỗi trùng lặp như một trường hợp đặc biệt nếu nó không được coi là 'success'
+                        if num_duplicates > 0:
+                             upload_record.status = 'failed' # Vẫn là failed vì process_new_patients_csv trả về success=False
+                             if num_duplicates > 3:
+                                 flash(f'Xử lý thất bại. Phát hiện trùng lặp {num_duplicates} bệnh nhân. Vui lòng kiểm tra file.', 'error')
+                             else:
+                                 colliding_names = ", ".join(duplicate_errors)
+                                 flash(f'Xử lý thất bại. Bệnh nhân đã tồn tại: {colliding_names}.', 'error')
+                        else:
+                            # Lỗi không xác định khác
+                            error_msg = result.get('error', 'Không có thông tin chi tiết')
+                            flash(f"Xử lý file thất bại. Lỗi không xác định. ({error_msg})", 'error')
+                            current_app.logger.error(f"Unknown CSV processing error for file ID {upload_record.id}: {error_msg}")
                 
                 db.session.commit()
                 
@@ -293,3 +287,36 @@ def delete(file_id):
         flash(f'Lỗi khi xóa file: {str(e)}', 'error')
     
     return redirect(url_for('upload.history'))
+
+# Route mới để tải template CSV
+@upload_bp.route('/download_template/<template_type>')
+@login_required
+def download_template(template_type):
+    output = io.StringIO()
+    writer = csv.writer(output)
+    filename = "template.csv"
+
+    if template_type == 'new_patients':
+        # Header dựa trên hàm generate_new_patient_upload_rows trong generate_patients.py
+        header = [
+            'firstName', 'lastName', 'age', 'gender', 'height', 'weight', 'blood_type'
+        ]
+        writer.writerow(header)
+        # Có thể thêm một dòng ví dụ (tùy chọn)
+        # writer.writerow(['John', 'Doe', 50, 'male', 175.0, 75.5, 'O+'])
+        filename = "new_patients_template.csv"
+    # Thêm các loại template khác ở đây nếu cần (ví dụ: encounter_measurements)
+    # elif template_type == 'encounter_measurements':
+    #     header = [...] 
+    #     writer.writerow(header)
+    #     filename = "encounter_measurements_template.csv"
+    else:
+        flash('Loại template không hợp lệ', 'error')
+        return redirect(url_for('upload.index'))
+
+    output.seek(0)
+    return Response(
+        output,
+        mimetype="text/csv",
+        headers={"Content-Disposition": f"attachment;filename={filename}"}
+    )
diff --git a/app/templates/_formhelpers.html b/app/templates/_formhelpers.html
new file mode 100644
index 0000000000000000000000000000000000000000..cbaddd4add11d0220952c58b5a911d8410bd3e7f
--- /dev/null
+++ b/app/templates/_formhelpers.html
@@ -0,0 +1,26 @@
+{% macro render_field(field, label_text=None, placeholder=None, class_=None) %}
+    <div class="form-group">
+        {{ field.label(label_text if label_text else field.label.text, class="block text-sm font-medium text-gray-700") }}
+        <div class="mt-1">
+            {% set field_class = class_ if class_ else "shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-300 rounded-md " + (" border-red-500" if field.errors else "") %}
+            {% if placeholder %}
+                {{ field(class=field_class, placeholder=placeholder) | safe }}
+            {% else %}
+                {{ field(class=field_class) | safe }}
+            {% endif %}
+            {% if field.errors %}
+                <p class="mt-2 text-sm text-red-600">{{ field.errors[0] }}</p>
+            {% endif %}
+        </div>
+    </div>
+{% endmacro %}
+
+{% macro render_field_readonly(field, label_text=None, value=None) %}
+    <div class="form-group">
+        {{ field.label(label_text if label_text else field.label.text, class="block text-sm font-medium text-gray-700") }}
+        <div class="mt-1">
+            <input type="text" id="{{ field.id }}" name="{{ field.name }}" value="{{ value if value is not none else field.data }}" readonly 
+                class="bg-gray-50 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-300 rounded-md">
+        </div>
+    </div>
+{% endmacro %} 
\ No newline at end of file
diff --git a/app/templates/_macros.html b/app/templates/_macros.html
index 33fb5493e74c4e4af19b56f5a3387423b7605b47..2102e42caf877b9227061f2b2f59d77059ef82d8 100644
--- a/app/templates/_macros.html
+++ b/app/templates/_macros.html
@@ -28,24 +28,28 @@
   </nav>
 {% endmacro %}
 
-{% macro status_badge(status) %}
-  {% if status == 'available' %}
-    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
-      Sẵn sàng
-    </span>
-  {% elif status == 'unavailable' %}
-    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
-      Không khả dụng
-    </span>
-  {% elif status == 'on_leave' %}
-    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
-      Đang nghỉ
-    </span>
-  {% else %}
-    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">
-      {{ status|title }}
+{% macro status_badge(status_value) %}
+    {% set status_str = status_value | string | upper %}
+    {% set badge_class = 'bg-gray-100 text-gray-800' %} {# Default #}
+    {% set status_text = status_str.replace('_', ' ').title() %}
+
+    {% if status_str == 'AVAILABLE' or status_str == 'ACTIVE' or status_str == 'COMPLETED' or status_str == 'SUCCESS' %}
+        {% set badge_class = 'bg-green-100 text-green-800' %}
+    {% elif status_str == 'UNAVAILABLE' or status_str == 'INACTIVE' or status_str == 'FAILED' or status_str == 'ERROR' %}
+        {% set badge_class = 'bg-red-100 text-red-800' %}
+    {% elif status_str == 'PENDING' or status_str == 'NEEDS_ASSESSMENT' or status_str == 'IN_PROGRESS' %}
+        {% set badge_class = 'bg-yellow-100 text-yellow-800' %}
+    {% elif status_str == 'ON_LEAVE' %}
+        {% set badge_class = 'bg-blue-100 text-blue-800' %}
+        {% set status_text = 'On Leave' %}
+    {% elif status_str == 'DRAFT' %}
+        {% set badge_class = 'bg-purple-100 text-purple-800' %}
+    {% elif status_str == 'ASSESSMENT_IN_PROGRESS' %}
+        {% set badge_class = 'bg-indigo-100 text-indigo-800' %}
+    {% endif %}
+    <span class="px-2.5 py-0.5 inline-flex text-xs leading-5 font-semibold rounded-full {{ badge_class }}">
+        {{ status_text }}
     </span>
-  {% endif %}
 {% endmacro %}
 
 {% macro patient_status_badge(status) %}
diff --git a/app/templates/admin/edit_profile.html b/app/templates/admin/edit_profile.html
new file mode 100644
index 0000000000000000000000000000000000000000..cbe1e54d4fb45ca0815a2a0142b5732e4f739df6
--- /dev/null
+++ b/app/templates/admin/edit_profile.html
@@ -0,0 +1,87 @@
+{% extends "base.html" %}
+{% from "_formhelpers.html" import render_field, render_field_readonly %}
+
+{% block title %}Chỉnh sửa hồ sơ Admin{% endblock %}
+
+{% block header %}Chỉnh sửa thông tin Admin{% endblock %}
+
+{% block content %}
+<div class="container mx-auto px-4 py-6">
+    <!-- Breadcrumb -->
+    <nav class="mb-6" aria-label="Breadcrumb">
+        <ol class="inline-flex items-center space-x-1 md:space-x-3">
+            <li class="inline-flex items-center">
+                <a href="{{ url_for('main.handle_root') }}" class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 transition duration-200">
+                    <svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path></svg>
+                    Trang chủ
+                </a>
+            </li>
+            <li>
+                <div class="flex items-center">
+                    <svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
+                    <a href="{{ url_for('auth.profile') }}" class="ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2 transition duration-200">Hồ sơ</a>
+                </div>
+            </li>
+            <li aria-current="page">
+                <div class="flex items-center">
+                    <svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
+                    <span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Chỉnh sửa hồ sơ</span>
+                </div>
+            </li>
+        </ol>
+    </nav>
+
+    <div class="bg-white shadow overflow-hidden sm:rounded-lg">
+        <div class="px-4 py-5 sm:px-6 border-b border-gray-200">
+            <h3 class="text-lg leading-6 font-medium text-gray-900">Chỉnh sửa thông tin cá nhân</h3>
+            <p class="mt-1 text-sm text-gray-500">Cập nhật hồ sơ quản trị viên của bạn.</p>
+        </div>
+
+        <div class="px-4 py-5 sm:p-6">
+            <form action="{{ url_for('auth.edit_profile') }}" method="POST" class="space-y-6">
+                {{ form.csrf_token }}
+                <input type="hidden" name="form_type" value="profile">
+
+                <div class="grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">
+                    <div class="sm:col-span-3">
+                        {{ render_field(form.firstName, label_text="Tên") }}
+                    </div>
+
+                    <div class="sm:col-span-3">
+                        {{ render_field(form.lastName, label_text="Họ") }}
+                    </div>
+
+                    <div class="sm:col-span-4">
+                        {{ render_field(form.email, label_text="Email") }}
+                    </div>
+
+                    <div class="sm:col-span-4">
+                        {{ render_field(form.phone, label_text="Số điện thoại") }}
+                    </div>
+
+                    <div class="sm:col-span-4">
+                        <div class="form-group">
+                            <label class="block text-sm font-medium text-gray-700">Vai trò</label>
+                            <div class="mt-1">
+                                <input type="text" readonly value="{{ current_user.role }}" 
+                                    class="bg-gray-50 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-300 rounded-md">
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="pt-5 border-t border-gray-200 mt-6">
+                    <div class="flex justify-end">
+                        <a href="{{ url_for('auth.profile') }}" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
+                            Hủy
+                        </a>
+                        <button type="submit" class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
+                            Lưu thay đổi
+                        </button>
+                    </div>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
+{% endblock %} 
\ No newline at end of file
diff --git a/app/templates/admin/profile.html b/app/templates/admin/profile.html
new file mode 100644
index 0000000000000000000000000000000000000000..8eb4d45b6735584cdb2f87acbd915072c8379ca4
--- /dev/null
+++ b/app/templates/admin/profile.html
@@ -0,0 +1,99 @@
+{% extends "base.html" %}
+
+{% block title %}Admin Profile - {{ super() }}{% endblock %}
+
+{% block header %}Admin Profile{% endblock %}
+
+{% block content %}
+<div class="container mx-auto px-4 py-6">
+    <!-- Breadcrumb -->
+    <nav class="mb-6" aria-label="Breadcrumb">
+        <ol class="inline-flex items-center space-x-1 md:space-x-3">
+            <li class="inline-flex items-center">
+                <a href="{{ url_for('main.handle_root') }}" class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-primary-600 transition duration-200">
+                    <svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path></svg>
+                    Dashboard
+                </a>
+            </li>
+            <li aria-current="page">
+                <div class="flex items-center">
+                    <svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
+                    <span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">My Profile</span>
+                </div>
+            </li>
+        </ol>
+    </nav>
+
+    <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg">
+        <div class="px-4 py-5 sm:px-6 border-b border-gray-200">
+            <div class="flex items-center justify-between">
+                <div>
+                    <h3 class="text-lg leading-6 font-medium text-gray-900">{{ current_user.full_name }}</h3>
+                    <p class="mt-1 max-w-2xl text-sm text-gray-500">Administrator Profile</p>
+                </div>
+                <div class="flex-shrink-0">
+                    <a href="{{ url_for('auth.edit_profile') }}" class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition duration-200">
+                        <svg class="-ml-1 mr-2 h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
+                        </svg>
+                        Edit Profile
+                    </a>
+                </div>
+            </div>
+        </div>
+        <div class="border-t border-gray-200 px-4 py-5 sm:p-0">
+            <dl class="sm:divide-y sm:divide-gray-200">
+                <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Full Name</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.full_name }}</dd>
+                </div>
+                <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Email Address</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.email }}</dd>
+                </div>
+                 <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Phone Number</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.phone or 'Not provided' }}</dd>
+                </div>
+                <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Role</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
+                        <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
+                            {{ current_user.role }}
+                        </span>
+                    </dd>
+                </div>
+                <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Account Created</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.created_at.strftime('%d %b %Y, %H:%M') if current_user.created_at else 'N/A' }}</dd>
+                </div>
+                 <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Last Login</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.last_login_at.strftime('%d %b %Y, %H:%M') if current_user.last_login_at else 'Never' }}</dd>
+                </div>
+                <!-- Admin specific sections -->
+                <div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    <dt class="text-sm font-medium text-gray-500">Admin Actions</dt>
+                    <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
+                        <div class="space-y-2">
+                            <a href="{{ url_for('auth.admin_users') }}" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium flex items-center">
+                                <svg class="w-4 h-4 mr-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" /></svg>
+                                User Management
+                            </a>
+                            {# Placeholder links - update when these routes exist #}
+                            <a href="#" class="text-gray-400 text-sm font-medium flex items-center cursor-not-allowed" title="System Logs (Not Implemented)">
+                                <svg class="w-4 h-4 mr-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
+                                System Logs
+                            </a>
+                             <a href="#" class="text-gray-400 text-sm font-medium flex items-center cursor-not-allowed" title="System Configuration (Not Implemented)">
+                                <svg class="w-4 h-4 mr-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
+                                System Configuration
+                            </a>
+                        </div>
+                    </dd>
+                </div>
+            </dl>
+        </div>
+    </div>
+</div>
+{% endblock %} 
\ No newline at end of file
diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html
new file mode 100644
index 0000000000000000000000000000000000000000..5e9bb8dc1cf245a6f1846665c315e8f016f5c9bd
--- /dev/null
+++ b/app/templates/auth/login.html
@@ -0,0 +1,39 @@
+{% extends "base_auth.html" %}
+{% from "_formhelpers.html" import render_field %}
+
+{% block title %}Đăng nhập{% endblock %}
+
+{% block card_title %}Đăng nhập vào tài khoản của bạn{% endblock %}
+
+{% block card_content %}
+<form method="POST" action="{{ url_for('auth.login') }}">
+    {{ form.csrf_token }}
+    
+    <div class="mb-4">
+        {{ render_field(form.email, placeholder='Địa chỉ email của bạn', class_='w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500') }}
+    </div>
+    
+    <div class="mb-6">
+        {{ render_field(form.password, placeholder='Mật khẩu của bạn', class_='w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500') }}
+        {# Remove Forgot Password Link #}
+        {#
+        <div class="text-right mt-1">
+            <a href="{{ url_for('auth.reset_password_request') }}" class="text-sm text-blue-600 hover:underline">Quên mật khẩu?</a>
+        </div>
+        #}
+    </div>
+
+    <div class="mb-4">
+        <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg transition duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
+            Đăng nhập
+        </button>
+    </div>
+
+    {# Remove Registration Link if needed
+    <div class="text-center text-sm text-gray-600">
+        Chưa có tài khoản? 
+        <a href="{{ url_for('auth.register') }}" class="font-medium text-blue-600 hover:underline">Đăng ký</a>
+    </div>
+    #}
+</form>
+{% endblock %} 
\ No newline at end of file
diff --git a/app/templates/base.html b/app/templates/base.html
index 236e46a6d930432ec10cd1bb08678afe7579c50d..f681991a2767c88b23345ee309144c8b4dc1fe62 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -99,7 +99,6 @@
             transition: width 0.3s ease;
             z-index: 40;
             box-shadow: 4px 0 15px rgba(0, 0, 0, 0.1);
-            overflow-x: hidden;
         }
         
         .sidebar.collapsed {
@@ -120,10 +119,10 @@
             position: absolute;
             top: 20px;
             right: -15px;
-            background: #2563eb;
+            background-color: #1e40af;
             color: white;
-            width: 30px;
-            height: 30px;
+            width: 32px;
+            height: 32px;
             border-radius: 50%;
             display: flex;
             align-items: center;
@@ -131,7 +130,16 @@
             box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
             cursor: pointer;
             z-index: 50;
-            border: 2px solid #e5e7eb;
+            border: 2px solid white;
+            transition: background-color 0.2s ease;
+        }
+        
+        .sidebar-toggle:hover {
+            background-color: #1e3a8a;
+        }
+        
+        .sidebar-toggle i {
+            pointer-events: none;
         }
         
         /* Navigation items */
@@ -140,26 +148,34 @@
             margin: 4px 0;
             overflow: hidden;
             white-space: nowrap;
-            display: flex;
-            align-items: center;
-            padding: 0.8rem 1rem;
-            height: 50px;
         }
         
-        .nav-item i {
-            margin-right: 0.75rem;
-            font-size: 1.2rem;
-            width: 24px;
-            text-align: center;
+        .nav-item a {
+            padding: 0.8rem 1rem;
+            height: 50px;
+            display: flex;
+            align-items: center;
+            width: 100%;
+            border-radius: 8px;
+            transition: background-color 0.2s ease;
+            color: white;
+            text-decoration: none;
         }
 
-        .nav-item:hover {
+        .nav-item a:hover {
             background-color: var(--primary-dark);
         }
         
-        .nav-item.active {
-            background-color: var(--primary-dark);
-            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+        .nav-item.active a {
+             background-color: var(--primary-dark);
+             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+        }
+
+        .nav-item a i {
+            font-size: 1.2rem;
+            width: 24px;
+            text-align: center;
+            margin-right: 0.75rem;
         }
         
         /* Cards and components */
@@ -241,12 +257,12 @@
         }
         
         /* Style khi sidebar thu gọn */
-        .sidebar.collapsed .nav-item {
+        .sidebar.collapsed .nav-item a {
             justify-content: center;
             padding: 0.8rem 0;
         }
-        
-        .sidebar.collapsed .nav-item i {
+
+        .sidebar.collapsed .nav-item a i {
             margin-right: 0;
             font-size: 1.6rem;
         }
@@ -278,68 +294,63 @@
             
             <nav>
                 <ul>
-                    <li>
-                        <a href="{{ url_for('dashboard.index') }}" class="nav-item flex items-center py-3 px-4 {% if request.endpoint == 'dashboard.index' %}active{% endif %}">
-                            <i class="fas fa-tachometer-alt mr-3 text-lg"></i> <span class="nav-text">Dashboard</span>
+                    {# Dashboard #}
+                    <li class="nav-item {% if request.endpoint == 'dashboard.index' %}active{% endif %}">
+                        <a href="{{ url_for('dashboard.index') }}">
+                            <i class="fas fa-tachometer-alt"></i> <span class="nav-text">Dashboard</span>
                         </a>
                     </li>
-                    <li>
-                        <a href="{{ url_for('patients.index') }}" class="nav-item flex items-center py-3 px-4 {% if request.endpoint and request.endpoint.startswith('patients.') and request.endpoint != 'patients.physical_measurements' %}active{% endif %}">
-                            <i class="fas fa-user-injured mr-3 text-lg"></i> <span class="nav-text">Patient</span>
+                    {# Patient #}
+                    <li class="nav-item {% if request.endpoint and request.endpoint.startswith('patients.') and request.endpoint != 'patients.physical_measurements' %}active{% endif %}">
+                        <a href="{{ url_for('patients.index') }}">
+                            <i class="fas fa-user-injured"></i> <span class="nav-text">Patient</span>
                         </a>
                     </li>
-                    {# Procedures (Dietitian Only) - Move before Reports #}
+                    {# Procedures (Dietitian Only) #}
                     {% if current_user.role == 'Dietitian' %}
-                    <li class="nav-item {% if request.endpoint.startswith('dietitian.list_procedures') %}active{% endif %}">
-                        <a href="{{ url_for('dietitian.list_procedures') }}" class="text-white hover:text-primary-light flex items-center w-full">
+                    <li class="nav-item {% if request.endpoint.startswith('dietitian.') and request.endpoint != 'dietitian.dashboard' %}active{% endif %}">
+                        <a href="{{ url_for('dietitian.list_my_procedures') }}">
                             <i class="fas fa-procedures"></i>
                             <span class="nav-text">Procedures</span>
                         </a>
                     </li>
                     {% endif %}
+                    {# Reports #}
                     <li class="nav-item {% if request.endpoint and request.endpoint.startswith('report.') %}active{% endif %}">
-                        <a href="{{ url_for('report.index') }}" class="text-white hover:text-primary-light flex items-center w-full">
+                        <a href="{{ url_for('report.index') }}">
                             <i class="fas fa-chart-bar"></i>
                             <span class="nav-text">Reports</span>
                         </a>
                     </li>
+                    {# Dietitians #}
                     <li class="nav-item {% if request.endpoint and request.endpoint.startswith('dietitians.') %}active{% endif %}">
-                        <a href="{{ url_for('dietitians.index') }}" class="text-white hover:text-primary-light flex items-center w-full">
+                        <a href="{{ url_for('dietitians.index') }}">
                             <i class="fas fa-user-md"></i>
                             <span class="nav-text">Dietitians</span>
                         </a>
                     </li>
-                    
+                    {# Upload CSV (Not for Dietitian) #}
                     {% if current_user.role != 'Dietitian' %}
                     <li class="nav-item {% if request.endpoint and request.endpoint.startswith('upload.') %}active{% endif %}">
-                        <a href="{{ url_for('upload.index') }}" class="text-white hover:text-primary-light flex items-center w-full">
+                        <a href="{{ url_for('upload.index') }}">
                             <i class="fas fa-upload"></i>
                             <span class="nav-text">Upload CSV</span>
                         </a>
                     </li>
                     {% endif %}
-                    {% if current_user.role == 'Admin' %}
-                         {# ... Admin items (Admin Panel, Upload Data đã bị xóa) ... #}
-                    {% endif %}
-                    {# Mục mới: Support (Cho cả Admin và Dietitian) #}
+                    {# Support #}
                     {% if current_user.role in ['Admin', 'Dietitian'] %}
-                    <li class="nav-item {% if request.endpoint == 'support.index' %}active{% endif %}">
-                        <a href="{{ url_for('support.index') }}" class="relative flex items-center w-full">
+                    <li class="nav-item relative {% if request.endpoint == 'support.index' %}active{% endif %}">
+                        <a href="{{ url_for('support.index') }}">
                             <i class="fas fa-headset"></i>
                             <span class="nav-text">Support</span>
-                            <span id="support-link-badge" class="absolute top-2 right-2 notification-badge hidden">0</span> 
+                            {# Điều chỉnh vị trí để badge không che lấp icon #}
+                            <span id="support-link-badge" 
+                                  class="absolute notification-badge hidden text-xs font-bold leading-none text-center whitespace-nowrap align-baseline rounded-full"
+                                  style="top: 0rem !important; right: 0rem !important; padding: 0.2em 0.4em; min-width: 18px; background-color: #ef4444;">0</span>
                         </a>
                     </li>
                     {% endif %}
-                    {# Mục Help giữ nguyên -> Comment out vì không có route #}
-                    {#
-                    <li class="nav-item {% if request.endpoint == 'main.help' %}active{% endif %}">
-                        <a href="{{ url_for('main.help') }}" class="flex items-center w-full">
-                            <i class="fas fa-question-circle"></i>
-                            <span class="nav-text">Help</span>
-                        </a>
-                    </li>
-                    #}
                 </ul>
             </nav>
         </div>
@@ -365,8 +376,8 @@
                                 
                                 <div id="notificationMenu" class="dropdown-menu absolute right-0 mt-2 w-72 bg-white rounded-lg shadow-lg z-50 py-2">
                                     <div class="flex items-center justify-between px-4 py-2 border-b border-gray-100">
-                                        <h5 class="font-semibold text-gray-700">Thông báo</h5>
-                                        <span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-lg text-xs">3 mới</span>
+                                        <h5 class="font-semibold text-gray-700">Notifications</h5>
+                                        <span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-lg text-xs">3 new</span>
                                     </div>
                                     
                                     <div class="max-h-64 overflow-y-auto">
@@ -426,13 +437,13 @@
                                 <div id="userDropdownMenu" class="dropdown-menu absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50">
                                     <div class="py-1" role="menu" aria-orientation="vertical" aria-labelledby="dropdownMenuButton">
                                         <a href="{{ url_for('auth.profile') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">
-                                            <i class="fas fa-user mr-2"></i> Xem Hồ sơ
+                                            <i class="fas fa-user mr-2"></i> Profile
                                         </a>
                                         <a href="{{ url_for('auth.edit_profile') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">
-                                            <i class="fas fa-user-edit mr-2"></i> Chỉnh sửa Hồ sơ
+                                            <i class="fas fa-user-edit mr-2"></i> Edit Profile
                                         </a>
                                         <a href="{{ url_for('auth.logout') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">
-                                            <i class="fas fa-sign-out-alt mr-2"></i> Đăng xuất
+                                            <i class="fas fa-sign-out-alt mr-2"></i> Logout
                                         </a>
                                     </div>
                                 </div>
@@ -489,7 +500,7 @@
     <!-- Footer -->
     <footer class="bg-white py-6 mt-12">
         <div class="container mx-auto px-4">
-            <p class="text-center text-gray-500 text-sm">&copy; {{ current_year|default(2023) }} CCU HTM - Hệ thống dinh dưỡng đơn vị chăm sóc tích cực</p>
+            <p class="text-center text-gray-500 text-sm">&copy; {{ current_year|default(2023) }} CCU HTM - Intensive Care Unit Nutrition Management System</p>
         </div>
     </footer>
 
@@ -602,14 +613,30 @@
             dropdownButtons.forEach(button => {
                 button.addEventListener('click', function(event) {
                     const dropdownMenu = this.nextElementSibling;
+                    const isNotificationButton = this.id === 'notificationButton';
+
                     // Close other open dropdowns
                     document.querySelectorAll('.dropdown-menu.visible').forEach(menu => {
                         if (menu !== dropdownMenu) {
                             menu.classList.remove('visible');
                         }
                     });
+                    
                     // Toggle current dropdown
+                    const becomingVisible = !dropdownMenu.classList.contains('visible');
                     dropdownMenu.classList.toggle('visible');
+
+                    // If it's the notification button AND it's becoming visible, mark all as read
+                    if (isNotificationButton && becomingVisible) {
+                        console.log("[Notifications] Dropdown opened, marking all as read...");
+                        markAllNotificationsReadAPI(); // Gọi hàm API mới
+                        // Cập nhật badge ngay lập tức để phản hồi nhanh
+                        if (notificationBadge) {
+                            notificationBadge.textContent = '0';
+                            notificationBadge.style.display = 'none'; 
+                        }
+                    }
+                    
                     event.stopPropagation(); 
                 });
             });
@@ -624,13 +651,36 @@
                  });
             });
             
-            // --- Notification Logic --- (giữ nguyên phần này)
+            // --- Notification Logic --- 
             const notificationButton = document.getElementById('notificationButton');
             const notificationMenu = document.getElementById('notificationMenu');
             const notificationBadge = notificationButton ? notificationButton.querySelector('.notification-badge') : null;
             const notificationList = notificationMenu ? notificationMenu.querySelector('.max-h-64') : null; // Div chứa danh sách thông báo
             const notificationCountSpan = notificationMenu ? notificationMenu.querySelector('.flex span') : null; // Span hiển thị số lượng
 
+            // Hàm gọi API mark-all-read
+            function markAllNotificationsReadAPI() {
+                 fetch('{{ url_for("notifications.mark_all_notifications_as_read") }}', { // Sử dụng url_for
+                    method: 'POST',
+                    headers: {
+                        'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content') 
+                    },
+                })
+                .then(response => response.json())
+                .then(data => {
+                    if (data.success) {
+                        console.log('[Notifications] Marked all as read successfully:', data.message);
+                        // Có thể fetch lại thông báo để cập nhật trạng thái is_read nếu cần
+                        // fetchNotifications(); 
+                    } else {
+                        console.error('[Notifications] Failed to mark all as read:', data.message);
+                    }
+                })
+                .catch(error => {
+                    console.error('[Notifications] Error calling mark all read API:', error);
+                });
+            }
+
             function fetchNotifications() {
                 if (!notificationButton || !notificationMenu || !notificationBadge || !notificationList || !notificationCountSpan) return;
                 
@@ -641,10 +691,10 @@
                         if (data.count > 0) {
                             notificationBadge.textContent = data.count;
                             notificationBadge.style.display = 'flex'; // Hiển thị badge nếu có thông báo
-                            notificationCountSpan.textContent = `${data.count} mới`; // Cập nhật số lượng trong dropdown
+                            notificationCountSpan.textContent = `${data.count} new`; // Cập nhật số lượng trong dropdown
                         } else {
                             notificationBadge.style.display = 'none'; // Ẩn badge nếu không có
-                            notificationCountSpan.textContent = 'Không có thông báo mới';
+                            notificationCountSpan.textContent = 'No new notifications';
                         }
 
                         // Xóa danh sách cũ và hiển thị danh sách mới
@@ -680,7 +730,7 @@
                             });
                             
                         } else {
-                            notificationList.innerHTML = '<p class="text-sm text-gray-500 px-4 py-3">Không có thông báo nào.</p>';
+                            notificationList.innerHTML = '<p class="text-sm text-gray-500 px-4 py-3">No notifications.</p>';
                         }
                     })
                     .catch(error => {
@@ -796,10 +846,20 @@
     <!-- Support Message Unread Count Script -->
     <script>
         document.addEventListener('DOMContentLoaded', function() {
-            // --- Support Message Unread Count --- 
             const supportLinkBadge = document.getElementById('support-link-badge');
+            const supportNavLink = document.querySelector('a[href="{{ url_for("support.index") }}"]'); // Find the support nav link
+            
             function fetchSupportUnreadCount() {
-                 fetch('{{ url_for("support.unread_count") }}') // API endpoint for unread count
+                 // Only fetch if the badge element exists
+                 if (!supportLinkBadge) return;
+                 
+                 // Do not fetch/show count if currently on the support page
+                 if (window.location.pathname === '{{ url_for("support.index") }}') {
+                     supportLinkBadge.classList.add('hidden');
+                     return; 
+                 }
+                 
+                 fetch('{{ url_for("support.unread_count") }}') 
                     .then(response => {
                         if (!response.ok) {
                              throw new Error(`HTTP error! status: ${response.status}`);
@@ -807,25 +867,34 @@
                         return response.json();
                      })
                     .then(data => {
-                        if (supportLinkBadge) {
-                            const count = data.count || 0;
-                            supportLinkBadge.textContent = count;
-                            if (count > 0) {
-                                supportLinkBadge.classList.remove('hidden');
-                            } else {
-                                supportLinkBadge.classList.add('hidden');
-                            }
+                        const count = data.count || 0;
+                        supportLinkBadge.textContent = count;
+                        if (count > 0) {
+                            supportLinkBadge.classList.remove('hidden');
+                        } else {
+                            supportLinkBadge.classList.add('hidden');
                         }
                     })
                     .catch(error => console.error('Error fetching support unread count:', error));
             }
 
+            // Hide badge immediately if on support page on initial load
+            if (window.location.pathname === '{{ url_for("support.index") }}' && supportLinkBadge) {
+                supportLinkBadge.classList.add('hidden');
+            }
+            
             // Fetch support count initially and periodically if the badge exists
             if (supportLinkBadge) { 
-                 fetchSupportUnreadCount();
-                 setInterval(fetchSupportUnreadCount, 45000); // Check every 45 seconds
+                 fetchSupportUnreadCount(); // Fetch on load (if not on support page)
+                 setInterval(fetchSupportUnreadCount, 30000); // Check every 30 seconds
+            }
+            
+            // Add listener to hide badge when navigating TO support page (for SPA-like behavior if any)
+            if (supportNavLink && supportLinkBadge) {
+                supportNavLink.addEventListener('click', function() {
+                    supportLinkBadge.classList.add('hidden'); 
+                });
             }
-            // --- End Support Message Unread Count ---
         });
     </script>
 </body>
diff --git a/app/templates/base_auth.html b/app/templates/base_auth.html
new file mode 100644
index 0000000000000000000000000000000000000000..9bbc0f9db79860ae11e0fe31d2f181c80742a9d9
--- /dev/null
+++ b/app/templates/base_auth.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html lang="vi">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>{% block title %}CCU HTM{% endblock %}</title>
+    <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
+    <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
+    {% block head_extra %}{% endblock %}
+</head>
+<body class="bg-gradient-to-br from-blue-50 via-white to-indigo-100 flex items-center justify-center min-h-screen">
+    <div class="w-full max-w-md p-8 space-y-6 bg-white shadow-xl rounded-lg border border-gray-200">
+        <div class="text-center">
+            <a href="{{ url_for('main.handle_root') }}" class="inline-block mb-4">
+                 {# You can add a logo here if you have one #}
+                 <span class="text-2xl font-bold text-indigo-600">CCU HTM</span>
+            </a>
+            <h2 class="text-2xl font-bold text-gray-800">{% block card_title %}Authentication{% endblock %}</h2>
+        </div>
+
+         <!-- Flash messages -->
+         {% with messages = get_flashed_messages(with_categories=true) %}
+            {% if messages %}
+                <div class="space-y-2">
+                {% for category, message in messages %}
+                    <div class="p-3 rounded-md flex items-start 
+                        {% if category == 'error' or category == 'danger' %}bg-red-50 border border-red-200
+                        {% elif category == 'warning' %}bg-yellow-50 border border-yellow-200
+                        {% elif category == 'success' %}bg-green-50 border border-green-200
+                        {% else %}bg-blue-50 border border-blue-200{% endif %}">
+                        <div class="flex-shrink-0">
+                            {% if category == 'error' or category == 'danger' %}
+                                <i class="fas fa-times-circle text-red-500"></i>
+                            {% elif category == 'warning' %}
+                                <i class="fas fa-exclamation-triangle text-yellow-500"></i>
+                            {% elif category == 'success' %}
+                                <i class="fas fa-check-circle text-green-500"></i>
+                            {% else %}
+                                <i class="fas fa-info-circle text-blue-500"></i>
+                            {% endif %}
+                        </div>
+                        <div class="ml-3">
+                            <p class="text-sm font-medium 
+                                {% if category == 'error' or category == 'danger' %}text-red-800
+                                {% elif category == 'warning' %}text-yellow-800
+                                {% elif category == 'success' %}text-green-800
+                                {% else %}text-blue-800{% endif %}">
+                                {{ message }}
+                            </p>
+                        </div>
+                    </div>
+                {% endfor %}
+                </div>
+            {% endif %}
+        {% endwith %}
+
+        {% block card_content %}
+        {% endblock %}
+    </div>
+    {% block scripts %}{% endblock %}
+</body>
+</html> 
\ No newline at end of file
diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html
index bb387345e04a1ac88f6c59f8d6994fa365653f7e..810df42f37cc578df517b4914547d6cb469ff7fb 100644
--- a/app/templates/dashboard.html
+++ b/app/templates/dashboard.html
@@ -189,9 +189,22 @@
     </div>
     {% endif %}
 
-    <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
-        <!-- Recent Patients -->
-        <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg animate-fade-in" style="animation-delay: 0.6s">
+    <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
+        <!-- Patient Assignment Overview & Recent Patients (HÀNG 1) -->
+        <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg lg:col-span-2 animate-fade-in" style="animation-delay: 0.6s">
+            <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-indigo-50 to-white">
+                <h2 class="text-lg font-medium text-gray-900">Patient Assignment Overview</h2>
+            </div>
+            <div class="border-t border-gray-200 p-4 flex flex-col">
+                <div class="flex-grow mb-4" style="height: 380px;">
+                    <canvas id="dietitianWorkloadChart"></canvas>
+                </div>
+                <div id="workloadLegend" class="text-xs text-center space-y-1 mt-auto">
+                    {# Legend will be generated by JS #}
+                </div>
+            </div>
+        </div>
+        <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg lg:col-span-1 animate-fade-in" style="animation-delay: 0.65s">
             <div class="px-4 py-5 sm:px-6 flex justify-between items-center bg-gradient-to-r from-blue-50 to-white">
                 <h2 class="text-lg font-medium text-gray-900">Recent Patients</h2>
                 <a href="{{ url_for('patients.index') }}" class="text-sm text-blue-600 hover:text-blue-500 flex items-center">
@@ -243,22 +256,68 @@
             </div>
         </div>
 
-        <!-- BMI Distribution -->
-        <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg animate-fade-in" style="animation-delay: 0.7s">
+        <!-- HÀNG 2: 3 BIỂU ĐỒ STATUS (Full Width) -->
+        <div class="lg:col-span-3 grid grid-cols-1 md:grid-cols-3 gap-8 mb-8"> 
+            <!-- Patient Status Distribution -->
+            <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg animate-fade-in" style="animation-delay: 0.75s">
+            <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-yellow-50 to-white">
+                <h2 class="text-lg font-medium text-gray-900">Patient Status</h2>
+            </div>
+            <div class="border-t border-gray-200 p-4">
+                <div class="h-64" id="patientStatusChartContainer">
+                    <canvas id="patientStatusChart"></canvas>
+                </div>
+            </div>
+        </div>
+
+            <!-- Report Status Distribution -->
+            <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg animate-fade-in" style="animation-delay: 0.8s">
+            <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-purple-50 to-white">
+                <h2 class="text-lg font-medium text-gray-900">Report Status</h2>
+            </div>
+            <div class="border-t border-gray-200 p-4">
+                <div class="h-64" id="reportStatusChartContainer">
+                    <canvas id="reportStatusChart"></canvas>
+                </div>
+            </div>
+        </div>
+
+            <!-- Referral Status Distribution -->
+            <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg animate-fade-in" style="animation-delay: 0.85s">
+                <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-pink-50 to-white">
+                <h2 class="text-lg font-medium text-gray-900">Referral Status</h2>
+            </div>
+            <div class="border-t border-gray-200 p-4">
+                <div class="h-64" id="referralStatusChartContainer">
+                    <canvas id="referralStatusChart"></canvas>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- HÀNG 3: BMI Distribution (Full Width, Centered) -->
+        <div class="lg:col-span-3 bg-white shadow-lg overflow-hidden sm:rounded-lg mb-8 animate-fade-in" style="animation-delay: 0.7s">
             <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-green-50 to-white">
                 <h2 class="text-lg font-medium text-gray-900">BMI Distribution</h2>
             </div>
-            <div class="border-t border-gray-200 p-4">
-                <div class="h-64">
+            <div class="border-t border-gray-200 p-4 flex justify-center"> {# Giữ flex justify-center #}
+                <div class="h-64 w-full max-w-xs" id="bmiChartContainer"> {# Giảm max-width thành max-w-xs #}
                     <canvas id="bmiChart"></canvas>
                 </div>
             </div>
         </div>
 
-        <!-- Referrals Timeline -->
-        <div class="bg-white shadow-lg overflow-hidden sm:rounded-lg lg:col-span-2 animate-fade-in" style="animation-delay: 0.8s">
-            <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-blue-50 to-white">
-                <h2 class="text-lg font-medium text-gray-900">Referrals Timeline (Last 90 Days)</h2>
+        <!-- HÀNG 4: Referrals Timeline (Full Width) -->
+        <div class="lg:col-span-3 bg-white shadow-lg overflow-hidden sm:rounded-lg animate-fade-in" style="animation-delay: 0.9s">
+            <div class="px-4 py-5 sm:px-6 bg-gradient-to-r from-blue-50 to-white flex justify-between items-center">
+                <h2 class="text-lg font-medium text-gray-900">Referrals Timeline</h2>
+                <!-- Nút chọn Range -->
+                <div class="flex space-x-1" id="timeline-range-selector">
+                    <button data-range="day" class="px-2 py-1 text-xs font-medium rounded-md text-gray-600 bg-gray-100 hover:bg-gray-200 transition">Today</button>
+                    <button data-range="week" class="px-2 py-1 text-xs font-medium rounded-md text-gray-600 bg-gray-100 hover:bg-gray-200 transition">Last Week</button>
+                    <button data-range="month" class="px-2 py-1 text-xs font-medium rounded-md text-gray-600 bg-gray-100 hover:bg-gray-200 transition">Last Month</button>
+                    <button data-range="all" class="px-2 py-1 text-xs font-medium rounded-md text-white bg-blue-600 transition">All Time</button> {# Active state #}
+                </div>
             </div>
             <div class="border-t border-gray-200 p-4">
                 <div class="h-64">
@@ -272,84 +331,172 @@
 
 {% block scripts %}
 <script>
-    document.addEventListener('DOMContentLoaded', function() {
-        // BMI Chart with animation
-        var bmiCtx = document.getElementById('bmiChart').getContext('2d');
-        var bmiData = {
-            labels: [
-                {% for category, count in bmi_stats %}
-                '{{ category }}',
-                {% endfor %}
-            ],
-            datasets: [{
-                label: 'Number of Patients',
-                data: [
-                    {% for category, count in bmi_stats %}
-                    {{ count }},
-                    {% endfor %}
-                ],
-                backgroundColor: [
-                    'rgba(255, 99, 132, 0.7)',
-                    'rgba(54, 162, 235, 0.7)',
-                    'rgba(255, 206, 86, 0.7)',
-                    'rgba(75, 192, 192, 0.7)',
-                    'rgba(153, 102, 255, 0.7)'
-                ],
-                borderColor: [
-                    'rgba(255, 99, 132, 1)',
-                    'rgba(54, 162, 235, 1)',
-                    'rgba(255, 206, 86, 1)',
-                    'rgba(75, 192, 192, 1)',
-                    'rgba(153, 102, 255, 1)'
-                ],
-                borderWidth: 1
-            }]
-        };
-        var bmiChart = new Chart(bmiCtx, {
-            type: 'doughnut',
-            data: bmiData,
-            options: {
-                responsive: true,
-                maintainAspectRatio: false,
-                plugins: {
-                    legend: {
-                        position: 'right'
-                    },
-                    tooltip: {
-                        backgroundColor: 'rgba(0, 0, 0, 0.7)',
-                        padding: 10,
-                        titleColor: '#fff',
-                        titleFont: {
-                            size: 14
-                        },
-                        bodyColor: '#fff',
-                        bodyFont: {
-                            size: 13
-                        }
-                    }
+    // Helper function to generate colors
+    function generateColors(numColors) {
+        const colors = [
+            'rgba(54, 162, 235, 0.7)', // Blue
+            'rgba(75, 192, 192, 0.7)', // Green
+            'rgba(255, 206, 86, 0.7)', // Yellow
+            'rgba(153, 102, 255, 0.7)', // Purple
+            'rgba(255, 99, 132, 0.7)',  // Red
+            'rgba(255, 159, 64, 0.7)',  // Orange
+            'rgba(199, 199, 199, 0.7)', // Grey
+            'rgba(83, 109, 254, 0.7)',  // Indigo
+            'rgba(236, 64, 122, 0.7)'   // Pink
+        ];
+        const result = [];
+        for (let i = 0; i < numColors; i++) {
+            result.push(colors[i % colors.length]);
+        }
+        return result;
+    }
+
+    // Function to render a chart or display an error/no data message
+    function renderChart(ctx, chartId, chartType, data, options = {}) {
+        const container = document.getElementById(chartId + 'Container');
+        if (!ctx) {
+            console.error(`Chart context not found for ${chartId}`);
+            if (container) container.innerHTML = `<p class="text-center text-sm text-red-500 py-4">Error: Canvas element not found.</p>`;
+            return null;
+        }
+        try {
+            if (!data || (Array.isArray(data) && data.length === 0) || (typeof data === 'object' && Object.keys(data).length === 0)) {
+                 console.log(`${chartId} data is empty.`);
+                 if (container) container.innerHTML = `<p class="text-center text-sm text-gray-500 py-4">No data available.</p>`;
+                 return null;
+            }
+
+            // Basic default options (can be customized per chart)
+        const defaultOptions = {
+            responsive: true,
+            maintainAspectRatio: false,
+            plugins: {
+                    legend: { position: 'top', labels: { font: { size: 12 } } },
+                    tooltip: { bodyFont: { size: 12 } }
                 },
-                animation: {
-                    animateScale: true,
-                    animateRotate: true
+            };
+            
+            // Specific options based on type
+            if (chartType === 'bar') {
+                defaultOptions.indexAxis = 'y'; // Horizontal bar
+                defaultOptions.scales = { 
+                    x: { beginAtZero: true, ticks: { precision: 0 } },
+                    y: { ticks: { font: { size: 10 } } } 
+                };
+                defaultOptions.plugins.legend.display = false; // Often hide legend for simple bar
+            } else if (chartType === 'pie' || chartType === 'doughnut') {
+                defaultOptions.plugins.tooltip.callbacks = {
+                        label: function(context) {
+                        let label = context.label || '';
+                        let value = context.parsed;
+                        let total = context.dataset.data.reduce((acc, val) => acc + val, 0);
+                        let percentage = total > 0 ? ((value / total) * 100).toFixed(1) + '%' : '0.0%';
+                        return `${label}: ${value} (${percentage})`;
+                    }
                 }
+            } else if (chartType === 'line') {
+                 defaultOptions.scales = {
+                     x: { display: true, title: { display: true, text: 'Date', font: { size: 14, weight: 'bold' } }, ticks: { maxTicksLimit: 10, maxRotation: 45, minRotation: 45 }, grid: { display: false } },
+                     y: { display: true, title: { display: true, text: 'Number of Referrals', font: { size: 14, weight: 'bold' } }, beginAtZero: true, ticks: { precision: 0, stepSize: 1 }, grid: { color: 'rgba(0, 0, 0, 0.05)' } } // Ensure integer steps
+                 };
+                 defaultOptions.animation = { duration: 1500, easing: 'easeOutQuart' };
             }
-        });
+
+            const chartOptions = { ...defaultOptions, ...options }; // Merge specific options
+
+            return new Chart(ctx, {
+                type: chartType,
+                data: data,
+                options: chartOptions
+            });
+
+        } catch (error) {
+            console.error(`Error loading ${chartId}:`, error);
+            if (container) container.innerHTML = `<p class="text-center text-sm text-red-500 py-4">Error loading chart.</p>`;
+            return null;
+        }
+    }
+
+    // Store chart instances
+    let referralChartInstance = null;
+    let fullTimelineData = []; // Store the full data from backend
+
+    // Helper: Convert YYYY-MM-DD string to Date object at UTC midnight
+    function parseISODate(isoDateString) {
+        const parts = isoDateString.split('-').map(Number);
+        return new Date(Date.UTC(parts[0], parts[1] - 1, parts[2])); 
+    }
+
+    // Helper: Format Date object to YYYY-MM-DD string
+    function formatISODate(date) {
+        return date.toISOString().split('T')[0];
+    }
+
+    // Function to update the timeline chart based on selected range
+    function updateTimelineChart(range) {
+        const today = new Date();
+        today.setUTCHours(0, 0, 0, 0); // Use UTC midnight
+
+        let rangeStartDate = null;
+        const rangeEndDate = new Date(today); // Use a copy
+
+        // Determine the start date for the desired range
+        switch (range) {
+            case 'day':
+                rangeStartDate = new Date(today);
+                break;
+            case 'week':
+                rangeStartDate = new Date(today);
+                rangeStartDate.setUTCDate(today.getUTCDate() - 6); // 7 days including today
+                break;
+            case 'month':
+                rangeStartDate = new Date(today);
+                rangeStartDate.setUTCMonth(today.getUTCMonth() - 1);
+                // Ensure day is valid if previous month was shorter
+                if (rangeStartDate.getUTCMonth() === today.getUTCMonth()) {
+                     rangeStartDate.setUTCDate(0); // Last day of previous month
+                }
+                break;
+            case 'all':
+            default:
+                // Find the earliest date in the full data if it exists
+                if (fullTimelineData.length > 0) {
+                    // Sort dates to find the earliest reliably (dates are strings)
+                    const sortedDates = [...fullTimelineData].sort((a, b) => a.date.localeCompare(b.date));
+                    let earliestDate = parseISODate(sortedDates[0].date);
+                    // Set rangeStartDate to one day BEFORE the earliest date
+                    rangeStartDate = new Date(earliestDate);
+                    rangeStartDate.setUTCDate(earliestDate.getUTCDate() - 1);
+                } else {
+                    rangeStartDate = new Date(today); // Default to today if no data
+                }
+                break;
+        }
+
+        // Create a map of counts from the full data for quick lookup
+        const countsMap = new Map(fullTimelineData.map(item => [item.date, item.count]));
+
+        // Generate all dates within the calculated range
+        const filteredData = [];
+        let currentDate = new Date(rangeStartDate);
+        while (currentDate <= rangeEndDate) {
+            const currentDateISO = formatISODate(currentDate);
+            filteredData.push({
+                date: currentDateISO,
+                count: countsMap.get(currentDateISO) || 0 // Get count or default to 0
+            });
+            currentDate.setUTCDate(currentDate.getUTCDate() + 1); // Move to the next day
+        }
+
+        // Prepare labels and counts for the chart
+        const timelineLabels = filteredData.map(item => item.date);
+        const timelineCounts = filteredData.map(item => item.count);
         
-        // Referrals Timeline Chart with animation
-        var referralCtx = document.getElementById('referralChart').getContext('2d');
-        var referralData = {
-            labels: [
-                {% for item in referral_timeline[-30:] %}
-                '{{ item.date }}',
-                {% endfor %}
-            ],
+        const chartData = {
+            labels: timelineLabels,
             datasets: [{
                 label: 'Referrals',
-                data: [
-                    {% for item in referral_timeline[-30:] %}
-                    {{ item.count }},
-                    {% endfor %}
-                ],
+                data: timelineCounts,
                 backgroundColor: 'rgba(54, 162, 235, 0.2)',
                 borderColor: 'rgba(54, 162, 235, 1)',
                 borderWidth: 2,
@@ -357,79 +504,250 @@
                 tension: 0.4,
                 pointBackgroundColor: 'rgba(54, 162, 235, 1)',
                 pointBorderColor: '#fff',
-                pointBorderWidth: 2,
-                pointRadius: 4,
-                pointHoverRadius: 6,
-                pointHoverBackgroundColor: 'rgba(54, 162, 235, 1)',
-                pointHoverBorderColor: '#fff'
+                pointHoverRadius: 6
             }]
         };
-        var referralChart = new Chart(referralCtx, {
-            type: 'line',
-            data: referralData,
-            options: {
-                responsive: true,
-                maintainAspectRatio: false,
+
+        // Update or create chart
+        const ctx = document.getElementById('referralChart')?.getContext('2d');
+        if (!ctx) {
+            console.error("Referral chart context not found");
+            return;
+        }
+
+        if (referralChartInstance) {
+            referralChartInstance.data = chartData;
+            // Adjust x-axis ticks based on range
+            if (range === 'day' || filteredData.length <= 7) {
+                 referralChartInstance.options.scales.x.ticks.maxTicksLimit = undefined; // Show all ticks for short ranges
+            } else if (range === 'week' || filteredData.length <= 10) {
+                 referralChartInstance.options.scales.x.ticks.maxTicksLimit = 7;
+            } else {
+                 referralChartInstance.options.scales.x.ticks.maxTicksLimit = 10; // Default for longer ranges
+            }
+            referralChartInstance.update();
+        } else {
+            // Initial chart creation options (including integer Y-axis)
+            const initialOptions = {
                 scales: {
-                    x: {
-                        display: true,
-                        title: {
-                            display: true,
-                            text: 'Date',
-                            font: {
-                                size: 14,
-                                weight: 'bold'
+                     x: { display: true, title: { display: true, text: 'Date', font: { size: 14, weight: 'bold' } }, ticks: { maxTicksLimit: 10, maxRotation: 45, minRotation: 45 }, grid: { display: false } },
+                     y: { display: true, title: { display: true, text: 'Number of Referrals', font: { size: 14, weight: 'bold' } }, beginAtZero: true, ticks: { precision: 0, stepSize: 1 }, grid: { color: 'rgba(0, 0, 0, 0.05)' } } 
+                 },
+                 animation: { duration: 1500, easing: 'easeOutQuart' }
+            };
+            referralChartInstance = renderChart(ctx, 'referralChart', 'line', chartData, initialOptions);
+        }
+        
+        // Update button styles
+        const buttons = document.querySelectorAll('#timeline-range-selector button');
+        buttons.forEach(button => {
+             if (button.getAttribute('data-range') === range) {
+                 button.classList.remove('text-gray-600', 'bg-gray-100', 'hover:bg-gray-200');
+                 button.classList.add('text-white', 'bg-blue-600');
+             } else {
+                 button.classList.remove('text-white', 'bg-blue-600');
+                 button.classList.add('text-gray-600', 'bg-gray-100', 'hover:bg-gray-200');
+             }
+        });
+    }
+
+    // Prepare data and render charts on DOMContentLoaded
+    document.addEventListener('DOMContentLoaded', function() {
+        // --- Get Contexts ---
+        const ctxPatientStatus = document.getElementById('patientStatusChart')?.getContext('2d');
+        const ctxBmi = document.getElementById('bmiChart')?.getContext('2d');
+        const ctxReferralStatus = document.getElementById('referralStatusChart')?.getContext('2d');
+        const ctxReportStatus = document.getElementById('reportStatusChart')?.getContext('2d');
+        const ctxWorkload = document.getElementById('dietitianWorkloadChart')?.getContext('2d');
+        // Context for timeline is retrieved inside updateTimelineChart
+
+        // --- Data from Backend ---
+        const patientStatusDataRaw = JSON.parse('{{ patient_status_stats | tojson | safe }}') || {};
+        const bmiDataRaw = JSON.parse('{{ bmi_stats | tojson | safe }}') || [];
+        const referralStatusDataRaw = JSON.parse('{{ referral_status_stats | tojson | safe }}') || {};
+        const reportStatusDataRaw = JSON.parse('{{ report_status_stats | tojson | safe }}') || {};
+        const workloadRawData = JSON.parse('{{ dietitian_workload | tojson | safe }}') || [];
+        fullTimelineData = JSON.parse('{{ referral_timeline | tojson | safe }}') || []; // Store full data
+
+        // --- Render Initial Charts (excluding timeline) ---
+        // 1. Patient Status Chart (Bar)
+        if (ctxPatientStatus) {
+             try {
+                 if (Object.keys(patientStatusDataRaw).length === 0) {
+                     ctxPatientStatus.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-gray-500 py-4">No patient status data available.</p>';
+                 } else {
+                    const patientStatusLabels = Object.keys(patientStatusDataRaw);
+                    const patientStatusValues = Object.values(patientStatusDataRaw);
+                    renderChart(ctxPatientStatus, 'patientStatusChart', 'bar', {
+                        labels: patientStatusLabels,
+                    datasets: [{
+                            label: 'Patient Count',
+                            data: patientStatusValues,
+                            backgroundColor: generateColors(patientStatusLabels.length)
+                        }]
+                    });
+                 }
+             } catch (error) {
+                 console.error("Error loading Patient Status chart:", error);
+                 ctxPatientStatus.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-red-500 py-4">Error loading chart.</p>';
+            }
+        }
+
+        // 2. BMI Chart (Pie)
+        const bmiEntries = Array.isArray(bmiDataRaw) ? bmiDataRaw.filter(item => Array.isArray(item) && item.length === 2 && item[0] != null) : [];
+        if (ctxBmi) {
+            try {
+                 if (bmiEntries.length === 0) {
+                     ctxBmi.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-gray-500 py-4">No BMI data available.</p>';
+                 } else {
+                    const bmiLabels = bmiEntries.map(entry => entry[0]);
+                    const bmiValues = bmiEntries.map(entry => entry[1]);
+                    renderChart(ctxBmi, 'bmiChart', 'pie', {
+                        labels: bmiLabels,
+            datasets: [{
+                            label: 'Patients',
+                            data: bmiValues,
+                            backgroundColor: generateColors(bmiLabels.length)
+            }]
+                    });
+                }
+            } catch (error) {
+                console.error("Error loading BMI chart:", error);
+                ctxBmi.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-red-500 py-4">Error loading chart.</p>';
+            }
+        }
+
+        // 3. Referral Status Chart (Bar)
+        const referralStatusLabels = Object.keys(referralStatusDataRaw);
+        const referralStatusValues = Object.values(referralStatusDataRaw);
+         if (ctxReferralStatus) {
+             try {
+                 if (referralStatusLabels.length === 0) {
+                     ctxReferralStatus.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-gray-500 py-4">No referral status data available.</p>';
+                 } else {
+                    const referralStatusChart = renderChart(ctxReferralStatus, 'referralStatusChart', 'bar', {
+                        labels: referralStatusLabels,
+                        datasets: [{
+                            label: 'Referral Count',
+                            data: referralStatusValues,
+                            backgroundColor: generateColors(referralStatusLabels.length)
+                        }]
+                    });
+                    // Add click handler
+                    if (referralStatusChart) {
+                         ctxReferralStatus.canvas.onclick = function(evt) {
+                        const points = referralStatusChart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true);
+                        if (points.length) {
+                            const firstPoint = points[0];
+                                const originalStatus = referralStatusLabels[firstPoint.index];
+                                const encodedStatus = encodeURIComponent(originalStatus || '');
+                                window.location.href = `{{ url_for('patients.index') }}?referral_status=${encodedStatus}`;
                             }
-                        },
-                        ticks: {
-                            maxTicksLimit: 10,
-                            maxRotation: 45,
-                            minRotation: 45
-                        },
-                        grid: {
-                            display: false
-                        }
-                    },
-                    y: {
-                        display: true,
-                        title: {
-                            display: true,
-                            text: 'Number of Referrals',
-                            font: {
-                                size: 14,
-                                weight: 'bold'
+                         };
+                         ctxReferralStatus.canvas.style.cursor = 'pointer';
+                    }
+                }
+            } catch (error) {
+                 console.error("Error loading Referral Status chart:", error);
+                 ctxReferralStatus.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-red-500 py-4">Error loading chart.</p>';
+            }
+        }
+
+        // 4. Report Status Chart (Doughnut)
+        const reportStatusLabels = Object.keys(reportStatusDataRaw);
+        const reportStatusValues = Object.values(reportStatusDataRaw);
+         if (ctxReportStatus) {
+             try {
+                 if (reportStatusLabels.length === 0) {
+                     ctxReportStatus.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-gray-500 py-4">No report status data available.</p>';
+                } else {
+                    const reportStatusChart = renderChart(ctxReportStatus, 'reportStatusChart', 'doughnut', {
+                        labels: reportStatusLabels,
+                        datasets: [{
+                            label: 'Reports',
+                            data: reportStatusValues,
+                            backgroundColor: generateColors(reportStatusLabels.length)
+                        }]
+                    });
+                    // Add click handler
+                    if (reportStatusChart) {
+                        ctxReportStatus.canvas.onclick = function(evt) {
+                            const points = reportStatusChart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true);
+                            if (points.length) {
+                                const firstPoint = points[0];
+                                const originalStatus = reportStatusLabels[firstPoint.index];
+                                const encodedStatus = encodeURIComponent(originalStatus || '');
+                                window.location.href = `{{ url_for('report.index') }}?status=${encodedStatus}`;
                             }
-                        },
-                        beginAtZero: true,
-                        grid: {
-                            color: 'rgba(0, 0, 0, 0.05)'
-                        }
+                        };
+                        ctxReportStatus.canvas.style.cursor = 'pointer';
                     }
-                },
-                plugins: {
-                    tooltip: {
-                        backgroundColor: 'rgba(0, 0, 0, 0.7)',
-                        padding: 10,
-                        titleColor: '#fff',
-                        titleFont: {
-                            size: 14
-                        },
-                        bodyColor: '#fff',
-                        bodyFont: {
-                            size: 13
+                }
+            } catch (error) {
+                console.error("Error loading Report Status chart:", error);
+                ctxReportStatus.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-red-500 py-4">Error loading chart.</p>';
+            }
+        }
+
+        // 5. Dietitian Workload Chart (Pie with Custom Legend)
+        const validWorkloadData = Array.isArray(workloadRawData) ? workloadRawData.filter(item => item && item.name != null && item.count != null) : [];
+        if(ctxWorkload) {
+            try {
+                if (validWorkloadData.length === 0) {
+                     ctxWorkload.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-gray-500 py-4">No workload data available.</p>';
+                } else {
+                    const workloadLabels = validWorkloadData.map(item => item.name === null || item.name === 'null' ? 'Unassigned' : item.name);
+                const workloadCounts = validWorkloadData.map(item => item.count);
+                const workloadColors = generateColors(workloadLabels.length);
+                    const workloadChart = renderChart(ctxWorkload, 'dietitianWorkloadChart', 'pie', {
+                        labels: workloadLabels,
+                        datasets: [{
+                            label: 'Assigned Patients',
+                            data: workloadCounts,
+                            backgroundColor: workloadColors,
+                            borderColor: workloadColors.map(color => color.replace('0.7', '1')),
+                            borderWidth: 1
+                        }]
+                    }, { plugins: { legend: { display: false } } }); // Disable default legend
+                    // Generate custom legend
+                    if (workloadChart) {
+                const legendContainer = document.getElementById('workloadLegend');
+                        if (legendContainer) {
+                            legendContainer.innerHTML = ''; // Clear previous
+                validWorkloadData.forEach((item, index) => {
+                    const legendItem = document.createElement('div');
+                                legendItem.classList.add('inline-flex', 'items-center', 'mr-3', 'mb-1');
+                    legendItem.innerHTML = `
+                        <span class="w-3 h-3 mr-1.5 rounded-full flex-shrink-0" style="background-color: ${workloadColors[index].replace('0.7','1')}"></span>
+                                    <span class="truncate">${workloadLabels[index]}: ${item.count}</span>
+                    `;
+                    legendContainer.appendChild(legendItem);
+                });
                         }
-                    },
-                    legend: {
-                        display: true,
-                        position: 'top'
                     }
-                },
-                animation: {
-                    duration: 2000,
-                    easing: 'easeOutQuart'
+                }
+            } catch(error) {
+                 console.error("Error loading Workload chart:", error);
+                 ctxWorkload.canvas.parentElement.innerHTML = '<p class="text-center text-sm text-red-500 py-4">Error loading chart.</p>';
+            }
+        }
+
+        // --- 6. Initialize Referrals Timeline Chart (with 'all' data) ---
+        updateTimelineChart('all'); // Initial render with full data
+
+        // --- Add Event Listeners for Timeline Range Buttons ---
+        const rangeSelector = document.getElementById('timeline-range-selector');
+        if (rangeSelector) {
+            rangeSelector.addEventListener('click', function(event) {
+                if (event.target.tagName === 'BUTTON') {
+                    const range = event.target.getAttribute('data-range');
+                    if (range) {
+                        updateTimelineChart(range);
                 }
             }
         });
+        }
+
     });
 </script>
 {% endblock %}
diff --git a/app/templates/dietitian_procedures.html b/app/templates/dietitian_procedures.html
index 3af96d7f14bfb8049c94a55966fb24d2c91afc7f..cacbc7fcce601dd78aed4d61266db11cf6405523 100644
--- a/app/templates/dietitian_procedures.html
+++ b/app/templates/dietitian_procedures.html
@@ -9,12 +9,17 @@
 
     <div class="mb-6 flex justify-between items-center">
         <h1 class="text-3xl font-bold text-gray-800">Manage Procedures</h1>
-        {# Có thể thêm nút Add Procedure ở đây nếu muốn #}
-        {# <a href="#" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Add New Procedure</a> #}
+        {# Hiển thị nút Add Procedure chỉ khi đã chọn bệnh nhân cụ thể #}
+        {% if selected_patient_id %}
+            <a href="{{ url_for('dietitian.new_procedure', patient_id=selected_patient_id) }}" 
+               class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded inline-flex items-center">
+                <i class="fas fa-plus mr-2"></i> Add Procedure
+            </a>
+        {% endif %}
     </div>
 
     <!-- Filter Form -->
-    <form method="GET" action="{{ url_for('.list_procedures') }}" class="mb-6 bg-white shadow-md rounded px-8 pt-6 pb-8">
+    <form method="GET" action="{{ url_for('.list_my_procedures') }}" class="mb-6 bg-white shadow-md rounded px-8 pt-6 pb-8">
         <div class="flex flex-wrap gap-4 items-end">
             <div class="flex-grow">
                 <label for="patient_id" class="block text-gray-700 text-sm font-bold mb-2">Filter by Patient:</label>
@@ -27,7 +32,7 @@
             </div>
             <div class="flex items-end space-x-2">
                 <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">Filter</button>
-                 <a href="{{ url_for('.list_procedures') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">Reset</a>
+                 <a href="{{ url_for('.list_my_procedures') }}" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">Reset</a>
             </div>
         </div>
     </form>
@@ -92,7 +97,7 @@
 
     <!-- Pagination -->
     <div class="mt-6">
-        {{ render_pagination(pagination, '.list_procedures', filter_args={'patient_id': selected_patient_id}) }}
+        {{ render_pagination(pagination, '.list_my_procedures', filter_args={'patient_id': selected_patient_id}) }}
     </div>
 </div>
 
diff --git a/app/templates/dietitians/new.html b/app/templates/dietitians/new.html
index dea99df4f03e6655b09390a17a75e52052115d98..9613147ad0983349d59383d4cb779ed39d81596d 100644
--- a/app/templates/dietitians/new.html
+++ b/app/templates/dietitians/new.html
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% from "_formhelpers.html" import render_field %}
 
 {% block title %}Thêm chuyên gia dinh dưỡng mới - Admin{% endblock %}
 
@@ -31,73 +32,32 @@
 
     <h1 class="text-2xl font-semibold text-gray-800 mb-6">Tạo tài khoản chuyên gia dinh dưỡng mới</h1>
 
-    {# Lưu ý: Route dietitians.new hiện tại có thể chưa xử lý hết các field này, cần cập nhật route #}
     <form method="POST" action="{{ url_for('dietitians.new') }}" class="bg-white shadow-md rounded-lg px-8 pt-6 pb-8 mb-4">
-        {# Assume CSRF token is handled #}
+        {{ form.csrf_token }}
 
         <h3 class="text-lg font-medium text-gray-900 mb-4 border-b pb-2">Thông tin tài khoản (User)</h3>
         <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
             <!-- First Name -->
-            <div>
-                <label for="firstName" class="block text-sm font-medium text-gray-700 mb-1">Họ <span class="text-red-500">*</span></label>
-                <input type="text" id="firstName" name="firstName" required 
-                       class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-            </div>
-
+            {{ render_field(form.firstName, label_text="Họ") }}
             <!-- Last Name -->
-            <div>
-                <label for="lastName" class="block text-sm font-medium text-gray-700 mb-1">Tên <span class="text-red-500">*</span></label>
-                <input type="text" id="lastName" name="lastName" required 
-                       class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-            </div>
-
+            {{ render_field(form.lastName, label_text="Tên") }}
             <!-- Email -->
-            <div>
-                <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email <span class="text-red-500">*</span></label>
-                <input type="email" id="email" name="email" required 
-                       class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-            </div>
-            
+            {{ render_field(form.email, label_text="Email") }}
             <!-- Password -->
-            <div>
-                <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Mật khẩu <span class="text-red-500">*</span></label>
-                <input type="password" id="password" name="password" required 
-                       class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-                 <p class="text-xs text-gray-500 mt-1">Mật khẩu tạm thời cho người dùng mới.</p>
-            </div>
+            {{ render_field(form.password, label_text="Mật khẩu") }}
+            <div class="md:col-span-2"><p class="text-xs text-gray-500 -mt-4">Mật khẩu tạm thời cho người dùng mới.</p></div>
         </div>
 
         <h3 class="text-lg font-medium text-gray-900 mb-4 border-b pb-2">Thông tin hồ sơ (Dietitian Profile)</h3>
         <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
              <!-- Phone -->
-            <div>
-                <label for="phone" class="block text-sm font-medium text-gray-700 mb-1">Số điện thoại</label>
-                <input type="tel" id="phone" name="phone" 
-                       class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-            </div>
-
+            {{ render_field(form.phone, label_text="Số điện thoại") }}
             <!-- Specialization -->
-            <div>
-                <label for="specialization" class="block text-sm font-medium text-gray-700 mb-1">Chuyên môn</label>
-                <input type="text" id="specialization" name="specialization" 
-                       class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-            </div>
-            
-            <!-- Status -->
-             <div>
-                <label for="status" class="block text-sm font-medium text-gray-700 mb-1">Trạng thái <span class="text-red-500">*</span></label>
-                <select id="status" name="status" required class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md">
-                    {% for status_item in status_options %}
-                        <option value="{{ status_item.name }}" {% if status_item.name == 'AVAILABLE' %}selected{% endif %}>{{ status_item.value.replace('_', ' ').title() }}</option>
-                    {% endfor %}
-                </select>
-            </div>
-
+            {{ render_field(form.specialization, label_text="Chuyên môn") }}
              <!-- Notes -->
             <div class="md:col-span-2">
-                <label for="notes" class="block text-sm font-medium text-gray-700 mb-1">Ghi chú</label>
-                <textarea id="notes" name="notes" rows="3" 
-                          class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border border-gray-300 rounded-md"></textarea>
+                 <label for="notes" class="block text-sm font-medium text-gray-700 mb-1">Ghi chú</label>
+                 {{ form.notes(rows="3", class="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border border-gray-300 rounded-md") }}
             </div>
         </div>
 
diff --git a/app/templates/edit_profile.html b/app/templates/edit_profile.html
index c2ed5812fbe94d16f24db161bbc145fab4792762..9ae7bacbac1f6ff3e4eefce3037fbcdcf324dd08 100644
--- a/app/templates/edit_profile.html
+++ b/app/templates/edit_profile.html
@@ -1,181 +1,166 @@
 {% extends "base.html" %}
+{% from "_formhelpers.html" import render_field, render_field_readonly %}
 
-{% block title %}Chỉnh sửa hồ sơ - CCU HTM{% endblock %}
+{% block title %}Chỉnh sửa hồ sơ - {{ super() }}{% endblock %}
 
 {% block header %}Chỉnh sửa thông tin cá nhân{% endblock %}
 
 {% block content %}
 <div class="container mx-auto px-4 py-6">
     <!-- Breadcrumb -->
-    <div class="mb-6">
-        <nav class="flex" aria-label="Breadcrumb">
-            <ol class="inline-flex items-center space-x-1 md:space-x-3">
-                <li class="inline-flex items-center">
-                    <a href="{{ url_for('handle_root') }}" class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 transition duration-200">
-                        <svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path></svg>
-                        Trang chủ
-                    </a>
-                </li>
-                <li aria-current="page">
-                    <div class="flex items-center">
-                        <svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
-                        <span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Chỉnh sửa hồ sơ</span>
+    <nav class="mb-6" aria-label="Breadcrumb">
+        <ol class="inline-flex items-center space-x-1 md:space-x-3">
+            <li class="inline-flex items-center">
+                <a href="{{ url_for('main.handle_root') }}" class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 transition duration-200">
+                    <svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path></svg>
+                    Trang chủ
+                </a>
+            </li>
+             <li>
+                <div class="flex items-center">
+                    <svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
+                    <a href="{{ url_for('auth.profile') }}" class="ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2 transition duration-200">Hồ sơ của tôi</a>
+                </div>
+            </li>
+            <li aria-current="page">
+                <div class="flex items-center">
+                    <svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
+                    <span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Chỉnh sửa hồ sơ</span>
+                </div>
+            </li>
+        </ol>
+    </nav>
+
+    <div class="max-w-4xl mx-auto bg-white shadow-lg overflow-hidden sm:rounded-lg">
+        <!-- Tab navigation -->
+        <div class="border-b border-gray-200">
+            <nav class="-mb-px flex space-x-8 px-6" aria-label="Tabs">
+                <button id="profile-tab" type="button" class="border-indigo-500 text-indigo-600 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm" aria-current="page">
+                    Thông tin cá nhân
+                </button>
+                <button id="password-tab" type="button" class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
+                    Thay đổi mật khẩu
+                </button>
+            </nav>
+        </div>
+
+        <!-- Profile content -->
+        <div id="profile-content" class="px-4 py-5 sm:p-6">
+            <form action="{{ url_for('auth.edit_profile') }}" method="POST" class="space-y-6">
+                {{ form.csrf_token if form is defined }}
+                <input type="hidden" name="form_type" value="profile">
+                
+                <div class="grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">
+                    <div class="sm:col-span-3">
+                        {{ render_field(form.firstName, label_text="Tên") }}
                     </div>
-                </li>
-            </ol>
-        </nav>
-    </div>
 
-    <div class="max-w-4xl mx-auto">
-        <div class="bg-white shadow overflow-hidden sm:rounded-lg">
-            <div class="px-4 py-5 sm:px-6 border-b border-gray-200">
-                <h3 class="text-lg leading-6 font-medium text-gray-900">Chỉnh sửa thông tin cá nhân</h3>
-                <p class="mt-1 max-w-2xl text-sm text-gray-500">Cập nhật thông tin chi tiết của bạn.</p>
-            </div>
-
-            <div class="px-4 py-5 sm:p-6">
-                <!-- Tab navigation -->
-                <nav class="flex border-b border-gray-200">
-                    <button id="profile-tab" type="button" class="py-4 px-6 text-center border-b-2 border-primary-500 font-medium text-sm text-primary-600 bg-white" aria-current="page">
-                        Thông tin cá nhân
-                    </button>
-                    <button id="password-tab" type="button" class="py-4 px-6 text-center border-b-2 border-transparent font-medium text-sm text-gray-500 hover:text-gray-700 hover:border-gray-300 bg-white">
-                        Thay đổi mật khẩu
-                    </button>
-                </nav>
-
-                <!-- Profile content -->
-                <div id="profile-content" class="py-6">
-                    <form action="{{ url_for('auth.edit_profile') }}" method="POST" class="space-y-8 divide-y divide-gray-200">
-                        {{ form.csrf_token if form is defined }}
-                        <input type="hidden" name="form_type" value="profile">
-                        <div class="space-y-8 divide-y divide-gray-200">
-                            <div class="grid grid-cols-6 gap-6">
-                                <div class="col-span-6 sm:col-span-3">
-                                    <label for="firstName" class="block text-sm font-medium text-gray-700">Tên</label>
-                                    {{ form.firstName(id="firstName", class="mt-1 focus:ring-primary-500 focus:border-primary-500 block w-full shadow-sm sm:text-sm border border-gray-300 rounded-md", required=True) }}
-                                    {% if form.firstName.errors %}
-                                        <p class="mt-2 text-sm text-red-600">{{ form.firstName.errors[0] }}</p>
-                                    {% endif %}
-                                </div>
-
-                                <div class="col-span-6 sm:col-span-3">
-                                    <label for="lastName" class="block text-sm font-medium text-gray-700">Họ</label>
-                                    {{ form.lastName(id="lastName", class="mt-1 focus:ring-primary-500 focus:border-primary-500 block w-full shadow-sm sm:text-sm border border-gray-300 rounded-md", required=True) }}
-                                    {% if form.lastName.errors %}
-                                        <p class="mt-2 text-sm text-red-600">{{ form.lastName.errors[0] }}</p>
-                                    {% endif %}
-                                </div>
-
-                                <div class="col-span-6 sm:col-span-3">
-                                    <label for="email" class="block text-sm font-medium text-gray-700">Email</label>
-                                    {{ form.email(id="email", class="mt-1 focus:ring-primary-500 focus:border-primary-500 block w-full shadow-sm sm:text-sm border border-gray-300 rounded-md", required=True) }}
-                                    {% if form.email.errors %}
-                                        <p class="mt-2 text-sm text-red-600">{{ form.email.errors[0] }}</p>
-                                    {% endif %}
-                                </div>
-
-                                <div class="col-span-6 sm:col-span-3">
-                                    <label for="phone" class="block text-sm font-medium text-gray-700">Số điện thoại</label>
-                                    {{ form.phone(id="phone", class="mt-1 focus:ring-primary-500 focus:border-primary-500 block w-full shadow-sm sm:text-sm border border-gray-300 rounded-md") }}
-                                    {% if form.phone.errors %}
-                                        <p class="mt-2 text-sm text-red-600">{{ form.phone.errors[0] }}</p>
-                                    {% endif %}
-                                </div>
-
-                                {% if current_user.role == 'Dietitian' or (form.specialization is defined) %}
-                                <div class="col-span-6">
-                                    <label for="specialization" class="block text-sm font-medium text-gray-700">Chuyên môn</label>
-                                    {{ form.specialization(id="specialization", class="mt-1 focus:ring-primary-500 focus:border-primary-500 block w-full shadow-sm sm:text-sm border border-gray-300 rounded-md") }}
-                                    {% if form.specialization.errors %}
-                                        <p class="mt-2 text-sm text-red-600">{{ form.specialization.errors[0] }}</p>
-                                    {% endif %}
-                                </div>
-
-                                <div class="col-span-6">
-                                    <label for="status" class="block text-sm font-medium text-gray-700">Trạng thái</label>
-                                    <div class="mt-1 block w-full py-2 px-3 border border-gray-300 bg-gray-100 rounded-md shadow-sm text-sm text-gray-700">
-                                        {{ current_user.dietitian_profile.status.value if current_user.dietitian_profile else 'N/A' }}
-                                        <span class="text-xs text-gray-500 ml-2">(Tự động cập nhật)</span>
-                                    </div>
-                                </div>
-
-                                <div class="col-span-6">
-                                    <label for="notes" class="block text-sm font-medium text-gray-700">Ghi chú</label>
-                                    {{ form.notes(id="notes", rows="3", class="mt-1 focus:ring-primary-500 focus:border-primary-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md") }}
-                                    {% if form.notes.errors %}
-                                        <p class="mt-2 text-sm text-red-600">{{ form.notes.errors[0] }}</p>
-                                    {% endif %}
-                                </div>
-                                {% endif %}
-                            </div>
-                        </div>
-                        
-                        <div class="pt-5">
-                            <div class="flex justify-end">
-                                <button type="button" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" onclick="window.location.href='{{ url_for('auth.profile') }}'">
-                                    Hủy
-                                </button>
-                                <button type="submit" class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
-                                    Lưu thay đổi
-                                </button>
-                            </div>
+                    <div class="sm:col-span-3">
+                        {{ render_field(form.lastName, label_text="Họ") }}
+                    </div>
+
+                    <div class="sm:col-span-4">
+                         {{ render_field(form.email, label_text="Email") }}
+                    </div>
+
+                    <div class="sm:col-span-4">
+                         {{ render_field(form.phone, label_text="Số điện thoại") }}
+                    </div>
+
+                    {% if current_user.role == 'Dietitian' and current_user.dietitian %}
+                    <div class="sm:col-span-6 border-t border-gray-200 pt-6 mt-6">
+                         <h3 class="text-base font-semibold leading-6 text-gray-900">Thông tin Chuyên gia Dinh dưỡng</h3>
+                    </div>
+
+                    <div class="sm:col-span-4">
+                         <label class="block text-sm font-medium text-gray-700">Dietitian ID</label>
+                         <input type="text" value="{{ current_user.dietitian.formattedID }}" readonly 
+                                class="mt-1 block w-full bg-gray-50 shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm border-gray-300 rounded-md">
+                     </div>
+
+                    <div class="sm:col-span-4">
+                        <label class="block text-sm font-medium text-gray-700">Trạng thái</label>
+                        <input type="text" value="{{ current_user.dietitian.status.value.replace('_', ' ').title() }}" readonly 
+                               class="mt-1 block w-full bg-gray-50 shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm border-gray-300 rounded-md">
+                        <p class="mt-1 text-xs text-gray-500">Trạng thái được cập nhật tự động dựa trên số lượng bệnh nhân.</p>
+                     </div>
+
+                    <div class="sm:col-span-6">
+                        {{ render_field(form.specialization, label_text="Chuyên môn") }}
+                    </div>
+                    
+                    <div class="sm:col-span-6">
+                        <label for="notes" class="block text-sm font-medium text-gray-700">Ghi chú</label>
+                        <div class="mt-1">
+                            {{ form.notes(id="notes", rows="4", class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border border-gray-300 rounded-md") }}
                         </div>
-                    </form>
+                        {% if form.notes.errors %}<p class="mt-2 text-sm text-red-600">{{ form.notes.errors[0] }}</p>{% endif %}
+                    </div>
+                    {% endif %}
+                </div>
+                
+                <div class="pt-5 border-t border-gray-200 mt-6">
+                    <div class="flex justify-end">
+                        <a href="{{ url_for('auth.profile') }}" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
+                            Hủy
+                        </a>
+                        <button type="submit" class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
+                            Lưu thay đổi
+                        </button>
+                    </div>
+                </div>
+            </form>
+        </div>
+
+        <!-- Password content (Initially hidden) -->
+        <div id="password-content" class="px-4 py-5 sm:p-6 hidden">
+             <form action="{{ url_for('auth.change_password') }}" method="POST" class="space-y-6">
+                 <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
+                 <div>
+                    <h3 class="text-base font-semibold leading-6 text-gray-900">Thay đổi mật khẩu</h3>
+                    <p class="mt-1 text-sm text-gray-500">Cập nhật mật khẩu của bạn để bảo mật tài khoản.</p>
                 </div>
 
-                <!-- Password content (Initially hidden) -->
-                <div id="password-content" class="py-6 hidden">
-                    <form action="{{ url_for('auth.change_password') }}" method="POST" class="space-y-8 divide-y divide-gray-200">
-                        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
-                        <div class="space-y-8 divide-y divide-gray-200">
-                            <div>
-                                <div>
-                                    <h3 class="text-lg leading-6 font-medium text-gray-900">Thay đổi mật khẩu</h3>
-                                    <p class="mt-1 text-sm text-gray-500">Cập nhật mật khẩu của bạn để bảo mật tài khoản.</p>
-                                </div>
-
-                                <div class="mt-6 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6">
-                                    <div class="sm:col-span-4">
-                                        <label for="current_password" class="block text-sm font-medium text-gray-700">Mật khẩu hiện tại</label>
-                                        <div class="mt-1">
-                                            <input type="password" name="current_password" id="current_password" 
-                                                class="shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border border-gray-300 rounded-md" required>
-                                        </div>
-                                    </div>
-
-                                    <div class="sm:col-span-4">
-                                        <label for="new_password" class="block text-sm font-medium text-gray-700">Mật khẩu mới</label>
-                                        <div class="mt-1">
-                                            <input type="password" name="new_password" id="new_password" 
-                                                class="shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border border-gray-300 rounded-md" required>
-                                        </div>
-                                    </div>
-
-                                    <div class="sm:col-span-4">
-                                        <label for="confirm_password" class="block text-sm font-medium text-gray-700">Xác nhận mật khẩu mới</label>
-                                        <div class="mt-1">
-                                            <input type="password" name="confirm_password" id="confirm_password" 
-                                                class="shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border border-gray-300 rounded-md" required>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
+                <div class="grid grid-cols-1 gap-y-6 sm:grid-cols-6">
+                    <div class="sm:col-span-4">
+                        <label for="current_password" class="block text-sm font-medium text-gray-700">Mật khẩu hiện tại</label>
+                        <div class="mt-1">
+                            <input type="password" name="current_password" id="current_password" 
+                                class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border border-gray-300 rounded-md" required>
+                            {% if password_form and password_form.current_password.errors %}<p class="mt-2 text-sm text-red-600">{{ password_form.current_password.errors[0] }}</p>{% endif %}
                         </div>
-                        
-                        <div class="pt-5">
-                            <div class="flex justify-end">
-                                <button type="button" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" onclick="window.location.href='{{ url_for('auth.profile') }}'">
-                                    Hủy
-                                </button>
-                                <button type="submit" class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">
-                                    Lưu thay đổi
-                                </button>
-                            </div>
+                    </div>
+
+                    <div class="sm:col-span-4">
+                        <label for="new_password" class="block text-sm font-medium text-gray-700">Mật khẩu mới</label>
+                        <div class="mt-1">
+                            <input type="password" name="new_password" id="new_password" 
+                                class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border border-gray-300 rounded-md" required>
+                            {% if password_form and password_form.new_password.errors %}<p class="mt-2 text-sm text-red-600">{{ password_form.new_password.errors[0] }}</p>{% endif %}
+                        </div>
+                    </div>
+
+                    <div class="sm:col-span-4">
+                        <label for="confirm_password" class="block text-sm font-medium text-gray-700">Xác nhận mật khẩu mới</label>
+                        <div class="mt-1">
+                            <input type="password" name="confirm_password" id="confirm_password" 
+                                class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border border-gray-300 rounded-md" required>
+                            {% if password_form and password_form.confirm_password.errors %}<p class="mt-2 text-sm text-red-600">{{ password_form.confirm_password.errors[0] }}</p>{% endif %}
                         </div>
-                    </form>
+                    </div>
+                 </div>
+                        
+                <div class="pt-5 border-t border-gray-200 mt-6">
+                    <div class="flex justify-end">
+                         <button type="button" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" onclick="showProfileTab()">
+                            Hủy
+                        </button>
+                        <button type="submit" class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
+                            Cập nhật mật khẩu
+                        </button>
+                    </div>
                 </div>
-            </div>
+            </form>
         </div>
     </div>
 </div>
@@ -188,39 +173,49 @@
         const profileContent = document.getElementById('profile-content');
         const passwordContent = document.getElementById('password-content');
 
-        // Kiểm tra hash URL để hiển thị tab tương ứng
+        // Function to switch tabs
+        function switchTab(activeTab, inactiveTab, activeContent, inactiveContent, hash) {
+            activeTab.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300');
+            activeTab.classList.add('border-indigo-500', 'text-indigo-600');
+            activeTab.setAttribute('aria-current', 'page');
+
+            inactiveTab.classList.remove('border-indigo-500', 'text-indigo-600');
+            inactiveTab.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300');
+            inactiveTab.removeAttribute('aria-current');
+
+            activeContent.classList.remove('hidden');
+            inactiveContent.classList.add('hidden');
+
+            // Update URL hash without causing page jump if possible
+            if (history.pushState) {
+                history.pushState(null, null, hash ? '#' + hash : window.location.pathname + window.location.search);
+            } else {
+                window.location.hash = hash;
+            }
+        }
+
+        const showProfileTab = () => switchTab(profileTab, passwordTab, profileContent, passwordContent, '');
+        const showPasswordTab = () => switchTab(passwordTab, profileTab, passwordContent, profileContent, 'change-password');
+
+        // Check initial hash
         if (window.location.hash === '#change-password') {
             showPasswordTab();
+        } else {
+            showProfileTab(); // Default to profile tab
         }
 
+        // Add event listeners
         profileTab.addEventListener('click', showProfileTab);
         passwordTab.addEventListener('click', showPasswordTab);
 
-        function showProfileTab() {
-            profileTab.classList.add('border-primary-500', 'text-primary-600');
-            profileTab.classList.remove('border-transparent', 'text-gray-500');
-            passwordTab.classList.add('border-transparent', 'text-gray-500');
-            passwordTab.classList.remove('border-primary-500', 'text-primary-600');
-            
-            profileContent.classList.remove('hidden');
-            passwordContent.classList.add('hidden');
-            
-            // Cập nhật URL hash
-            window.location.hash = '';
-        }
-
-        function showPasswordTab() {
-            passwordTab.classList.add('border-primary-500', 'text-primary-600');
-            passwordTab.classList.remove('border-transparent', 'text-gray-500');
-            profileTab.classList.add('border-transparent', 'text-gray-500');
-            profileTab.classList.remove('border-primary-500', 'text-primary-600');
-            
-            passwordContent.classList.remove('hidden');
-            profileContent.classList.add('hidden');
-            
-            // Cập nhật URL hash
-            window.location.hash = 'change-password';
-        }
+        // Handle back/forward navigation
+        window.addEventListener('hashchange', function() {
+             if (window.location.hash === '#change-password') {
+                showPasswordTab();
+            } else {
+                showProfileTab();
+            }
+        });
     });
 </script>
 {% endblock %} 
\ No newline at end of file
diff --git a/app/templates/list_patient_procedures.html b/app/templates/list_patient_procedures.html
new file mode 100644
index 0000000000000000000000000000000000000000..6f1261ea5fe0c4de6b751319f6310eec3cd5bf3d
--- /dev/null
+++ b/app/templates/list_patient_procedures.html
@@ -0,0 +1,106 @@
+{% extends 'base.html' %}
+
+{% block title %}Procedures for {{ patient.full_name }} - Dietitian Area{% endblock %}
+
+{% block content %}
+<div class="container mx-auto px-4 py-6 animate-slide-in">
+
+    <div class="bg-white shadow-md rounded-lg p-6 mb-8">
+        <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 pb-4 border-b border-gray-200">
+            <div>
+                <h1 class="text-2xl font-bold text-gray-800">
+                    Procedures for: {{ patient.full_name }} ({{ patient.id }})
+                </h1>
+                 <p class="text-sm text-gray-500 mt-1">
+                     <a href="{{ url_for('patients.patient_detail', patient_id=patient.id) }}" class="text-primary-600 hover:underline">Back to Patient Detail</a>
+                 </p>
+             </div>
+            {# Nút Add Procedure #}
+            <a href="{{ url_for('.new_procedure', patient_id=patient.id) }}"
+               class="mt-3 sm:mt-0 inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-150 
+                      {% if has_ongoing_encounter %}bg-blue-600 hover:bg-blue-700{% else %}bg-gray-400 cursor-not-allowed{% endif %}"
+               {% if not has_ongoing_encounter %}aria-disabled="true" title="No active encounter to add procedure to." tabindex="-1" {% endif %}>
+                <i class="fas fa-plus mr-2"></i> Add New Procedure
+            </a>
+        </div>
+
+        {# Procedure Table #}
+        {% if procedures and procedures|length > 0 %}
+            <div class="overflow-x-auto">
+                <table class="min-w-full divide-y divide-gray-200">
+                    <thead class="bg-gray-50">
+                        <tr>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Performed Time</th>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">End Time</th>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Results</th>
+                            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Encounter</th>
+                            <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
+                        </tr>
+                    </thead>
+                    <tbody class="bg-white divide-y divide-gray-200">
+                        {% for proc in procedures %}
+                        <tr class="hover:bg-gray-50">
+                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ proc.procedureType }}</td>
+                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ proc.procedureName or 'N/A' }}</td>
+                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ proc.procedureDateTime.strftime('%d/%m/%Y %H:%M') if proc.procedureDateTime else 'N/A' }}</td>
+                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ proc.procedureEndDateTime.strftime('%d/%m/%Y %H:%M') if proc.procedureEndDateTime else 'N/A' }}</td>
+                            <td class="px-6 py-4 whitespace-normal text-sm text-gray-500 max-w-xs truncate" title="{{ proc.description or 'N/A' }}">{{ proc.description or 'N/A' }}</td>
+                            <td class="px-6 py-4 whitespace-normal text-sm text-gray-500 max-w-xs truncate" title="{{ proc.procedureResults or 'N/A' }}">{{ proc.procedureResults or 'N/A' }}</td>
+                            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+                                {% if proc.encounter %}
+                                <a href="{{ url_for('patients.encounter_measurements', patient_id=proc.patient_id, encounter_id=proc.encounter.encounterID) }}" class="text-blue-600 hover:underline">
+                                    {{ proc.encounter.custom_encounter_id or proc.encounter.encounterID }}
+                                </a>
+                                {% else %}
+                                N/A
+                                {% endif %}
+                            </td>
+                            <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
+                                {# Nút Edit #}
+                                <a href="{{ url_for('.edit_procedure', procedure_id=proc.id) }}"
+                                   class="text-indigo-600 hover:text-indigo-900 text-lg p-1 rounded-md hover:bg-indigo-100 transition duration-150 ease-in-out"
+                                   title="Edit Procedure">
+                                    <i class="fas fa-edit"></i>
+                                </a>
+                                {# Nút Delete (dùng JS confirm hoặc modal nếu có) #}
+                                <form action="{{ url_for('.delete_procedure', procedure_id=proc.id) }}" method="POST" class="inline needs-confirmation" data-confirmation-message="Are you sure you want to delete procedure #{{ proc.id }}?">
+                                    <input type="hidden" name="_method" value="DELETE"> {# Nếu dùng method override #}
+                                    {{ csrf_token() if csrf_token else '' }} {# Add CSRF token #}
+                                    <button type="submit"
+                                            class="text-red-600 hover:text-red-900 text-lg p-1 rounded-md hover:bg-red-100 transition duration-150 ease-in-out"
+                                            title="Delete Procedure">
+                                        <i class="fas fa-trash-alt"></i>
+                                    </button>
+                                </form>
+                            </td>
+                        </tr>
+                        {% endfor %}
+                    </tbody>
+                </table>
+            </div>
+        {% else %}
+            <p class="text-gray-500 italic text-center py-4">No procedures recorded for this patient yet.</p>
+        {% endif %}
+    </div>
+
+</div>
+
+{# Optional: Add JS for confirmation modal if needed #}
+<script>
+document.addEventListener('DOMContentLoaded', function() {
+    // Basic confirmation for delete buttons
+    document.querySelectorAll('form.needs-confirmation').forEach(form => {
+        form.addEventListener('submit', function(event) {
+            const message = this.getAttribute('data-confirmation-message') || 'Are you sure?';
+            if (!confirm(message)) {
+                event.preventDefault(); // Stop submission if user cancels
+            }
+        });
+    });
+});
+</script>
+
+{% endblock %} 
\ No newline at end of file
diff --git a/app/templates/login.html b/app/templates/login.html
index 0d1aef110b7ae05601cba7979ba7b7ca18830e01..ce62c09868201387ece0bde02cdea4ac01b632cb 100644
--- a/app/templates/login.html
+++ b/app/templates/login.html
@@ -150,10 +150,6 @@
                     Ghi nhớ đăng nhập
                 </label>
             </div>
-            
-            <a href="{{ url_for('auth.reset_password_request') }}" class="text-sm md:text-base text-blue-600 hover:text-blue-800 transition ml-4">
-                Quên mật khẩu?
-            </a>
         </div>
         
         <button type="submit" class="btn-login w-full border border-transparent rounded-md shadow-sm font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
diff --git a/app/templates/patient_detail.html b/app/templates/patient_detail.html
index d81f1a5e23df0bcd77f7930d86dff88fc1e53b6c..efe24ced7e78d210da42e51782c4e825b6867427 100644
--- a/app/templates/patient_detail.html
+++ b/app/templates/patient_detail.html
@@ -97,7 +97,7 @@
                         {% set p_status = patient.status %}
                         {% set p_color_map = {
                             PatientStatus.NOT_ASSESSED: 'gray',
-                            PatientStatus.NEEDS_ASSESSMENT: 'amber',
+                            PatientStatus.NEEDS_ASSESSMENT: 'red',
                             PatientStatus.ASSESSMENT_IN_PROGRESS: 'blue',
                             PatientStatus.COMPLETED: 'green'
                         } %}
@@ -239,11 +239,24 @@
                                     {# Định dạng lại BMI #}
                                     <div class="text-3xl font-bold text-gray-900">{{ "%.1f"|format(patient.bmi) if patient.bmi else '--' }}</div>
                                     <div class="mt-1 w-full bg-gray-200 rounded-full h-2">
-                                        {% set bmi_percentage = patient.get_bmi_percentage() %}
+                                        {# Tính toán lại phần trăm dựa trên thang đo 0-45 cho hiển thị #}
+                                        {% set visual_bmi_max = 45.0 %}
+                                        {% set bmi_value = patient.bmi if patient.bmi is not none else 0 %}
+                                        {% set bmi_percentage = ((bmi_value / visual_bmi_max) * 100) | round(1) %}
+                                        {% set bmi_percentage = 100 if bmi_percentage > 100 else bmi_percentage %}
                                         {# Lấy tên màu (ví dụ: 'blue', 'green') từ hàm helper #}
                                         {% set bmi_color_class_name = patient.get_bmi_color_class() or 'gray' %}
-                                        {# Sử dụng class động cho màu nền và style inline cho width, thêm dấu ; #}
-                                        <div class="h-2 rounded-full bg-{{ bmi_color_class_name }}-500" style="width: {{ bmi_percentage }}%;"></div>
+                                        {# Sử dụng class động cho màu nền và style inline cho width - SỬA LỖI LINTER (Attempt 3) #}
+                                        {# Tạo biến để lưu class màu #}
+                                        {% set bmi_bg_class = 'bg-gray-500' %} {# Default #}
+                                        {% if bmi_color_class_name == 'blue' %}{% set bmi_bg_class = 'bg-blue-500' %}
+                                        {% elif bmi_color_class_name == 'green' %}{% set bmi_bg_class = 'bg-green-500' %}
+                                        {% elif bmi_color_class_name == 'yellow' %}{% set bmi_bg_class = 'bg-yellow-500' %}
+                                        {% elif bmi_color_class_name == 'orange' %}{% set bmi_bg_class = 'bg-orange-500' %}
+                                        {% elif bmi_color_class_name == 'red' %}{% set bmi_bg_class = 'bg-red-500' %}
+                                        {% endif %}
+                                        {# In class vào thẻ div #}
+                                        <div class="h-2 rounded-full {{ bmi_bg_class }}" style="width: {{ bmi_percentage }}%;"></div>
                                     </div>
                                     <div class="mt-2 flex justify-between w-full text-xs text-gray-500">
                                         <span>0</span>
@@ -380,7 +393,7 @@
                                     <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Chuyên gia DD</th>
                                     <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Chỉ số bất ổn (Max 3)</th>
                                     <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Trạng thái</th>
-                                    <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Thao tác</th>
+                                    <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">THAO TÁC</th>
                                 </tr>
                             </thead>
                              <tbody class="bg-white divide-y divide-gray-200">
@@ -460,7 +473,7 @@
                                         <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Lý do</th>
                                         <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Trạng thái</th>
                                         <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Chuyên gia DD</th>
-                                        <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Thao tác</th>
+                                        <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">THAO TÁC</th>
                                     </tr>
                                 </thead>
                                 <tbody class="bg-white divide-y divide-gray-200">
@@ -537,20 +550,33 @@
         <div id="procedures" class="tab-pane">
             {# Apply Tailwind classes to the procedure content #}
             <div class="bg-white shadow rounded-lg overflow-hidden">
-                <div class="px-4 py-5 sm:px-6 flex justify-between items-center border-b border-gray-200">
-                    <h3 class="text-lg leading-6 font-medium text-gray-900">
+                {# Conditional Header for Procedures Tab #}
+                <div class="px-4 py-5 sm:px-6 flex flex-col sm:flex-row justify-between items-start sm:items-center border-b border-gray-200">
+                    <h3 class="text-lg leading-6 font-medium text-gray-900 mb-2 sm:mb-0">
                         Danh sách Thủ thuật
                     </h3>
-                    {# Check for ongoing encounter to enable button #}
-                    {% set has_ongoing_encounter = latest_encounter and latest_encounter.status == EncounterStatus.ON_GOING %}
-                    <a href="{{ url_for('dietitian.new_procedure', patient_id=patient.id) }}"
-                       class="inline-flex items-center px-3 py-1 border border-transparent rounded text-sm font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-150 
-                              {% if has_ongoing_encounter %}bg-blue-600 hover:bg-blue-700{% else %}bg-gray-400 cursor-not-allowed{% endif %}"
-                       {% if not has_ongoing_encounter %}aria-disabled="true" title="No active encounter to add procedure to."{% endif %}>
-                        <i class="fas fa-plus mr-1"></i> Thêm Thủ thuật
-                    </a>
+                    {# Check if procedures list exists and is not empty #}
+                    {% if procedures and procedures|length > 0 %}
+                        {# Display Add button ONLY if procedures exist #}
+                        {% set has_ongoing_encounter = latest_encounter and latest_encounter.status == EncounterStatus.ON_GOING %}
+                        <a href="{{ url_for('dietitian.new_procedure', patient_id=patient.id) }}"
+                           class="inline-flex items-center px-3 py-1 border border-transparent rounded text-sm font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-150 
+                                  {% if has_ongoing_encounter %}bg-blue-600 hover:bg-blue-700{% else %}bg-gray-400 cursor-not-allowed{% endif %}"
+                           {% if not has_ongoing_encounter %}aria-disabled="true" title="No active encounter to add procedure to." tabindex="-1"{% endif %}>
+                            <i class="fas fa-plus mr-1"></i> Thêm Thủ thuật
+                        </a>
+                    {% else %}
+                        {# Display text link if no procedures exist #}
+                        <p class="text-sm text-gray-600">
+                            No procedures yet! 
+                            <a href="{{ url_for('dietitian.list_my_procedures') }}" class="text-blue-600 hover:underline">Click to go to the procedures list.</a>
+                        </p>
+                    {% endif %}
                 </div>
+                {# End Conditional Header #}
+
                 <div class="px-4 py-5 sm:p-6">
+                    {# Keep the table rendering logic, it handles the empty case internally #}
                     {% if procedures and procedures|length > 0 %}
                         <div class="overflow-x-auto">
                             <table class="min-w-full divide-y divide-gray-200">
@@ -606,7 +632,8 @@
                             </table>
                         </div>
                     {% else %}
-                        <p class="text-gray-500 italic">No procedures recorded for this encounter.</p>
+                         {# Message already handled in the header part now #}
+                         <p class="text-gray-500 italic text-center py-4">No procedures recorded for this patient.</p>
                     {% endif %}
                 </div>
             </div>
@@ -614,130 +641,87 @@
 
             {# ... other sections ... #}
 
-        </div> {# End grid #}
-    </div> {# End container #}
-
-    <!-- Delete Procedure Confirmation Modal -->
-    <div id="deleteProcedureConfirmModal"
-         class="fixed inset-0 z-50 hidden flex items-center justify-center transition-opacity duration-300 ease-out"
-         aria-labelledby="deleteProcedureConfirmModalLabel"
-         aria-modal="true"
-         role="dialog">
-        <!-- Backdrop -->
-        <div class="fixed inset-0 bg-gray-900 bg-opacity-60 backdrop-blur-sm modal-backdrop transition-opacity duration-300 ease-out" aria-hidden="true"></div>
-
-        <!-- Modal Content -->
-        <div class="relative bg-white rounded-lg shadow-xl w-full max-w-md transform transition-all duration-300 ease-out scale-95 opacity-0 modal-content">
-            <div class="p-6">
-                <div class="flex items-start">
-                    <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
-                        <!-- Heroicon name: outline/exclamation-triangle -->
-                        <svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
-                            <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
-                        </svg>
-                    </div>
-                    <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left flex-grow">
-                        <h3 class="text-lg font-semibold leading-6 text-gray-900" id="deleteProcedureConfirmModalLabel">
-                            Xác nhận Xóa Thủ thuật
-                        </h3>
-                        <div class="mt-2">
-                            <p class="text-sm text-gray-600">
-                                Bạn có chắc chắn muốn xóa thủ thuật <strong id="procedure-info-placeholder" class="font-medium">[Procedure Info]</strong>?
-Hành động này không thể hoàn tác.
-                            </p>
-                        </div>
-                    </div>
-                     <button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center modal-close-btn">
-                        <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
-                        <span class="sr-only">Close modal</span>
-                    </button>
-                </div>
-            </div>
-            <div class="bg-gray-50 px-6 py-4 sm:flex sm:flex-row-reverse rounded-b-lg">
-                {# This form's action will be set by JavaScript #}
-                <form id="delete-procedure-form" action="#" method="POST" class="inline-block">
-                    {{ empty_form.csrf_token }} {# Use csrf_token from the passed form object #}
-                    <button id="confirm-delete-procedure-btn" type="submit" class="inline-flex w-full justify-center rounded-md bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto">
-                        Xóa
-                    </button>
-                </form>
-                <button type="button" class="mt-3 inline-flex w-full justify-center rounded-md bg-white px-4 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto modal-close-btn">
-                    Hủy bỏ
-                </button>
-            </div>
-        </div>
-    </div>
-{% endif %} {# Đóng khối if not current_user.is_admin cho tab Thủ thuật #}
+        </div> {# End Procedures Tab Pane #}
+        {% endif %}
 
-    <!-- Tab Báo cáo -->
+        <!-- Tab Báo cáo -->
     {% if not current_user.is_admin %}
     <div id="reports" class="tab-pane">
-        <div class="bg-white shadow-md rounded-lg overflow-hidden">
-            <div class="px-4 py-5 sm:px-6 border-b border-gray-200 flex justify-between items-center">
-                <h3 class="text-lg leading-6 font-medium text-gray-900">
-                    Danh sách Báo cáo Dinh dưỡng
-                </h3>
-            </div>
-            <div class="px-4 py-5 sm:p-6">
+            <div class="bg-white shadow-md rounded-lg overflow-hidden">
+                <div class="px-4 py-5 sm:px-6 border-b border-gray-200 flex justify-between items-center">
+                    <h3 class="text-lg leading-6 font-medium text-gray-900">
+                        Danh sách Báo cáo Dinh dưỡng
+                    </h3>
+                </div>
+                <div class="px-4 py-5 sm:p-6">
                 {% if reports %}
-                    <table class="min-w-full divide-y divide-gray-200">
-                        <thead class="bg-gray-50">
-                            <tr>
+                        <table class="min-w-full divide-y divide-gray-200">
+                            <thead class="bg-gray-50">
+                                <tr>
                                 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ENCOUNTER ID</th>
                                 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">CREATED DATE</th>
                                 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">IN-CHARGE DIETITIAN</th> 
                                 <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">STATUS</th>
                                 {# Change MORE back to THAO TAC and align right #}
                                 <th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">THAO TÁC</th> 
-                            </tr>
-                        </thead>
-                        <tbody class="bg-white divide-y divide-gray-200">
+                                </tr>
+                            </thead>
+                            <tbody class="bg-white divide-y divide-gray-200">
                             {% for report in reports|sort(attribute='report_date', reverse=True) %} {# Sắp xếp báo cáo theo ngày, mới nhất đầu tiên #}
-                                <tr class="hover:bg-gray-50 transition-colors duration-150">
-                                    <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
+                                    <tr class="hover:bg-gray-50 transition-colors duration-150">
+                                        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
                                         {# Hiển thị encounter ID, link tới report view #}
                                         <a href="{{ url_for('report.view_report', report_id=report.id) }}" class="text-primary-600 hover:text-primary-900 hover:underline">
                                             #{{ report.encounter.custom_encounter_id if report.encounter and report.encounter.custom_encounter_id else report.encounter_id }}
                                         </a>
-                                    </td>
-                                    <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ report.report_date.strftime('%d/%m/%Y %H:%M') if report.report_date else 'N/A' }}</td>
-                                    {# Hiển thị tên người tạo #}
+                                        </td>
+                                        <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ report.report_date.strftime('%d/%m/%Y %H:%M') if report.report_date else 'N/A' }}</td>
+                                    {# SỬA: Hiển thị dietitian được gán cho report, fallback về author #}
                                     <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                                         {# Ưu tiên hiển thị dietitian được gán cho report #}
                                         {{ report.dietitian.full_name if report.dietitian else (report.author.full_name if report.author else 'N/A') }}
                                     </td>
-                                    <td class="px-6 py-4 whitespace-nowrap">
-                                        {% set status_color = 'green' if report.status == 'Completed' else 'yellow' if report.status == 'Pending' else 'gray' if report.status == 'Draft' else 'gray' %}
-                                        <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-{{ status_color }}-100 text-{{ status_color }}-800">
-                                            {{ report.status }}
-                                        </span>
-                                    </td>
+                                        <td class="px-6 py-4 whitespace-nowrap">
+                                        {# Directly use status value and map colors #}
+                                        {% set status_value = report.status.value %}
+                                        {% set status_color_map = {
+                                            'Draft': 'gray',
+                                            'Pending': 'yellow',
+                                            'Completed': 'green'
+                                        } %}
+                                        {% set status_color = status_color_map.get(status_value, 'gray') %}
+                                            <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-{{ status_color }}-100 text-{{ status_color }}-800">
+                                            {{ status_value }}
+                                            </span>
+                                        </td>
                                     <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                         {# Bọc các actions vào div để căn chỉnh #}
                                         <div class="flex items-center justify-end space-x-2">
                                             {# Nút Xem #}
                                             <a href="{{ url_for('report.view_report', report_id=report.id) }}" class="text-blue-600 hover:text-blue-900" title="Xem chi tiết"><i class="fas fa-eye"></i></a>
                                             
-                                            {# Nút Sửa (chỉ khi Pending/Draft và có quyền) #}
-                                            {% if report.status in ['Pending', 'Draft'] and (current_user.is_admin or report.dietitian_id == current_user.userID) %}
+                                            {# SỬA: Nút Sửa (chỉ khi Pending/Draft và có quyền) #}
+                                            {# Điều kiện: report status là Draft hoặc Pending VÀ (user là admin HOẶC user là author HOẶC user là dietitian được gán) #}
+                                            {% set report_status = report.status.value if report.status else None %}
+                                            {% if report_status in ['Draft', 'Pending'] and (current_user.is_admin or (report.author_id == current_user.userID) or (report.dietitian_id == current_user.userID)) %}
                                             <a href="{{ url_for('report.edit_report', report_id=report.id) }}" class="text-indigo-600 hover:text-indigo-900" title="Chỉnh sửa"><i class="fas fa-edit"></i></a>
-                                        {% endif %}
+                                            {% endif %}
                                         </div>
-                                    </td>
-                                </tr>
-                            {% endfor %}
-                        </tbody>
-                    </table>
-                {% else %}
-                    <p class="text-center text-gray-500 py-6">Chưa có báo cáo nào cho bệnh nhân này.</p>
-                {% endif %}
+                                        </td>
+                                    </tr>
+                                {% endfor %}
+                            </tbody>
+                        </table>
+                    {% else %}
+                        <p class="text-center text-gray-500 py-6">Chưa có báo cáo nào cho bệnh nhân này.</p>
+                    {% endif %}
+                </div>
             </div>
         </div>
-    </div>
     {% endif %}
 
-    <!-- Các tab khác sẽ được thêm vào đây -->
-</div>
+        <!-- Các tab khác sẽ được thêm vào đây -->
+    </div>
 </div>
 
 {# Include confirmation modal macro #}
@@ -1227,10 +1211,10 @@ Hành động này không thể hoàn tác.
             // If target tab/pane doesn't exist, default to overview
             if (!tabToActivate || !paneToActivate) {
                 console.log(`[activateTab] Target ${targetId} not found, defaulting to ${defaultTabId}`);
-                tabToActivate = document.querySelector(`.tab-link[data-target="${defaultTabId}"]`);
-                paneToActivate = document.getElementById(defaultTabId);
-                targetId = defaultTabId; // Update targetId for hash update
-            }
+                 tabToActivate = document.querySelector(`.tab-link[data-target="${defaultTabId}"]`);
+                 paneToActivate = document.getElementById(defaultTabId);
+                 targetId = defaultTabId; // Update targetId for hash update
+             }
 
             if (tabToActivate && paneToActivate) {
                 console.log(`[activateTab] Activating tab ${targetId}`);
@@ -1244,7 +1228,7 @@ Hành động này không thể hoàn tác.
                 // Add animation class with a slight delay
                 setTimeout(() => {
                     if (paneToActivate.classList.contains('active')) {
-                        paneToActivate.classList.add('animate-fade-in-fast');
+                     paneToActivate.classList.add('animate-fade-in-fast');
                         console.log(`[activateTab] Applied animation to ${targetId}`);
                     }
                 }, 10); // Small delay
@@ -1254,10 +1238,10 @@ Hành động này không thể hoàn tác.
 
             // Update URL hash without scrolling
             if (history.replaceState) {
-                history.replaceState(null, null, `#${targetId}`);
-            } else {
-                window.location.hash = `#${targetId}`;
-            }
+                 history.replaceState(null, null, `#${targetId}`);
+             } else {
+                 window.location.hash = `#${targetId}`;
+             }
         }
 
         tabs.forEach(tab => {
@@ -1351,212 +1335,178 @@ Hành động này không thể hoàn tác.
         const backToChoiceBtn = document.getElementById('backToChoiceBtn');
         let assignDietitianModalInstance = null;
 
+        // INITIALIZE the modal instance FIRST
         if (assignModalEl && typeof bootstrap !== 'undefined') {
             assignDietitianModalInstance = new bootstrap.Modal(assignModalEl);
+            console.log("[Assign Modal] Instance created successfully.");
         } else {
-            console.error("Assign Dietitian Modal element or Bootstrap JS not found.");
+            console.error("[Assign Modal] Modal element or Bootstrap JS not found. Modal cannot be shown.");
         }
 
         // Function to switch views
         const showChoiceView = () => {
-            if (choiceView) choiceView.style.display = 'grid'; // Hoặc 'block' tùy layout
+            // ... (showChoiceView implementation remains the same)
+            if (choiceView) choiceView.style.display = 'grid';
             if (manualView) manualView.style.display = 'none';
             if (backToChoiceBtn) backToChoiceBtn.classList.add('hidden');
-            if (assignSubmitBtn) assignSubmitBtn.classList.add('hidden'); // Ẩn nút submit chính khi ở view lựa chọn
+            if (assignSubmitBtn) assignSubmitBtn.classList.add('hidden');
         };
         const showManualView = () => {
+            // ... (showManualView implementation remains the same)
             if (choiceView) choiceView.style.display = 'none';
             if (manualView) manualView.style.display = 'block';
             if (backToChoiceBtn) backToChoiceBtn.classList.remove('hidden');
-            if (assignSubmitBtn) assignSubmitBtn.classList.remove('hidden'); // Hiện nút submit khi ở view manual
+            if (assignSubmitBtn) assignSubmitBtn.classList.remove('hidden');
             if (assignmentTypeInput) assignmentTypeInput.value = 'manual';
         };
 
-        // Event listener for the main Assign Dietitian button
+        // Event listener for the main Assign Dietitian button (on detail page)
         if (assignDietitianBtn && assignDietitianModalInstance) {
-            assignDietitianBtn.addEventListener('click', () => {
-                console.log("[Assign Modal] Assign Dietitian button clicked."); // DEBUG
-                showChoiceView(); // Reset về view lựa chọn khi mở modal
-                assignDietitianModalInstance.show();
-            });
+             assignDietitianBtn.addEventListener('click', () => {
+                 console.log("[Assign Modal] Assign Dietitian button clicked on detail page.");
+                 showChoiceView(); // Reset to choice view
+
+                 // Update title and action based on current patient when clicking the button
+                 const assignPatientNameEl = document.querySelector('#assignDietitianModal #modal-title');
+                 const pagePatientName = document.querySelector('h2.text-2xl.font-bold')?.textContent.trim();
+                 if (assignPatientNameEl) {
+                    if(pagePatientName) {
+                       assignPatientNameEl.textContent = `Assign Dietitian for ${pagePatientName}`;
+                    } else {
+                       assignPatientNameEl.textContent = `Assign Dietitian`; // Fallback
+                    }
+                 }
+                 const patientIdMatch = window.location.pathname.match(/\/patients\/([^\/]+)/);
+                 const currentPatientId = patientIdMatch ? patientIdMatch[1] : null;
+                 if (assignForm && currentPatientId) {
+                    assignForm.action = `/patients/${currentPatientId}/assign_dietitian`;
+                    console.log("[Assign Modal] Updated form action on button click to: ", assignForm.action);
+                 } else {
+                    console.warn("[Assign Modal] Could not find assign form or patient ID to update action on button click.");
+                 }
+
+                 assignDietitianModalInstance.show();
+             });
         } else if (!assignDietitianBtn) {
-             console.error("[Assign Modal] Assign Dietitian button (#assignDietitianBtn) not found."); // DEBUG
+            console.log("[Assign Modal] Assign Dietitian button (#assignDietitianBtn) not found on this page."); 
         }
 
-        // Event listeners for buttons inside the modal
+        // Event listeners for buttons inside the modal (auto, manual, back, close)
+        // ... (listeners for chooseAutoBtn, chooseManualBtn, backToChoiceBtn, closeModalBtn remain the same) ...
         if (chooseAutoBtn && assignmentTypeInput && assignForm) {
             chooseAutoBtn.addEventListener('click', () => {
-                console.log("[Assign Modal] Auto Assign chosen."); // DEBUG
+                console.log("[Assign Modal] Auto Assign chosen.");
                 assignmentTypeInput.value = 'auto';
-                assignForm.submit(); // Submit form ngay lập tức
+                assignForm.submit();
             });
         }
         if (chooseManualBtn) {
             chooseManualBtn.addEventListener('click', () => {
-                console.log("[Assign Modal] Manual Assign chosen."); // DEBUG
+                console.log("[Assign Modal] Manual Assign chosen.");
                 showManualView();
             });
         }
         if (backToChoiceBtn) {
             backToChoiceBtn.addEventListener('click', () => {
-                console.log("[Assign Modal] Back button clicked."); // DEBUG
+                console.log("[Assign Modal] Back button clicked.");
                 showChoiceView();
             });
         }
         if (closeModalBtn && assignDietitianModalInstance) {
-            closeModalBtn.addEventListener('click', () => {
-                console.log("[Assign Modal] Cancel button clicked. Manually hiding modal elements."); // DEBUG
-                // *** BỎ HOÀN TOÀN LỆNH GỌI hide() ***
-                // try {
-                //     if (assignDietitianModalInstance) assignDietitianModalInstance.hide();
-                // } catch (e) {
-                //     console.error("[Assign Modal] Error during Bootstrap modalInstance.hide():", e);
-                // }
-                
-                // *** CHỈ DÙNG LOGIC ẨN THỦ CÔNG ***
-                if (assignModalEl) {
-                    assignModalEl.style.display = 'none';
+             closeModalBtn.addEventListener('click', () => {
+                 console.log("[Assign Modal] Cancel button clicked. Instance:", assignDietitianModalInstance); // Log the instance
+                 try {
+                    assignDietitianModalInstance.hide();
+                    console.log("[Assign Modal] hide() method called successfully.");
+                 } catch (error) {
+                     console.error("[Assign Modal] Error calling hide():", error);
+                 }
+                 // *** THÊM: Buộc ẩn modal thủ công ***
+                 if (assignModalEl) {
+                    console.log("[Assign Modal] Manually forcing display: none and removing classes.");
+                    assignModalEl.style.display = 'none'; 
                     assignModalEl.setAttribute('aria-hidden', 'true');
-                    assignModalEl.classList.remove('show', 'block', 'flex');
-                    
-                    const backdrop = document.querySelector('.modal-backdrop.fade.show'); 
-                    if (backdrop) backdrop.remove();
-                    
-                    // Xóa class và reset style cho body
-                    document.body.classList.remove('modal-open'); 
-                    document.body.style.overflow = '';
-                    document.body.style.paddingRight = '';
-                    console.log("[Assign Modal] Manual hide complete."); // DEBUG
-
-                    // *** THÊM: Dispose và Re-initialize modal instance ***
-                    if (assignDietitianModalInstance) {
-                        try {
-                            assignDietitianModalInstance.dispose();
-                            console.log("[Assign Modal] Modal instance disposed.");
-                            // Re-create the instance
-                            if (assignModalEl && typeof bootstrap !== 'undefined') {
-                                assignDietitianModalInstance = new bootstrap.Modal(assignModalEl);
-                                console.log("[Assign Modal] Modal instance re-initialized.");
-                            } else {
-                                console.error("[Assign Modal] Cannot re-initialize modal: Element or Bootstrap JS missing.");
-                            }
-                        } catch (e) {
-                             console.error("[Assign Modal] Error during modal dispose/re-initialize:", e);
-                        }
+                    assignModalEl.classList.remove('show'); 
+                     // Xóa backdrop nếu còn tồn tại
+                     const backdrop = document.querySelector('.modal-backdrop.fade.show');
+                     if (backdrop) {
+                        backdrop.remove();
+                        console.log("[Assign Modal] Manually removed backdrop.");
                     }
-                     // *** KẾT THÚC THÊM ***
-                } else {
-                    console.error("[Assign Modal] Cannot manually hide modal, element not found.");
+                    // Reset body style nếu Bootstrap thêm
+                    document.body.style.overflow = ''; 
+                    document.body.style.paddingRight = ''; 
                 }
-            });
+                // *** KẾT THÚC THÊM ***
+             });
+             console.log("[Assign Modal] Event listener added to closeModalBtn."); // Confirm listener attachment
+        } else {
+             if (!closeModalBtn) console.error("[Assign Modal] closeModalBtn not found.");
+             if (!assignDietitianModalInstance) console.error("[Assign Modal] assignDietitianModalInstance is null, cannot add listener to close button.");
         }
+
+        // Event listener for the final submit button (validation)
+        // ... (listener for assignSubmitBtn remains the same) ...
         if (assignSubmitBtn && assignForm) {
-             // Nút này chỉ hoạt động khi ở Manual View và đã chọn dietitian
-             // Việc submit form đã được xử lý bởi thẻ <form> và type="submit"
-             // Có thể thêm validation ở đây nếu cần
              assignSubmitBtn.addEventListener('click', (e) => {
                  const selectedDietitian = document.getElementById('dietitian_id');
                  if (assignmentTypeInput && assignmentTypeInput.value === 'manual' && selectedDietitian && !selectedDietitian.value) {
-                     e.preventDefault(); // Ngăn submit nếu chưa chọn dietitian
+                     e.preventDefault(); 
                      alert("Vui lòng chọn một chuyên gia dinh dưỡng.");
-                     console.log("[Assign Modal] Submit prevented: No dietitian selected."); // DEBUG
+                     console.log("[Assign Modal] Submit prevented: No dietitian selected."); 
                  } else {
-                      console.log("[Assign Modal] Submit button clicked (Manual assignment)."); // DEBUG
+                      console.log("[Assign Modal] Submit button clicked (Manual assignment)."); 
                  }
              });
         }
         // *** END: Logic for Assign Dietitian Modal ***
-
-        // Add event listeners for delete buttons
-        const deleteButtons = document.querySelectorAll('.delete-procedure-btn');
-        deleteButtons.forEach(button => {
-            button.addEventListener('click', () => {
-                const procId = button.getAttribute('data-proc-id');
-                const procInfo = button.getAttribute('data-proc-info');
-                const modalBody = document.querySelector('#deleteProcedureConfirmModal .modal-body #procedure-info-placeholder');
-                modalBody.textContent = `Thủ thuật: ${procInfo}`;
-                const confirmBtn = document.getElementById('confirm-delete-procedure-btn');
-                confirmBtn.addEventListener('click', () => {
-                    // Add your delete logic here
-                    console.log(`Thủ thuật ${procInfo} đã được xóa.`);
-                    button.closest('tr').remove();
-                    document.getElementById('deleteProcedureConfirmModal').modal('hide');
-                });
-            });
-        });
-
-        // *** START: Logic for Delete Procedure Modal (Revised for Tailwind) ***
-        const deleteProcedureModalEl = document.getElementById('deleteProcedureConfirmModal');
-        const modalBackdrop = deleteProcedureModalEl.querySelector('.modal-backdrop');
-        const modalContent = deleteProcedureModalEl.querySelector('.modal-content');
-        const deleteProcedureButtons = document.querySelectorAll('.delete-procedure-btn');
-        const procedureInfoPlaceholder = document.getElementById('procedure-info-placeholder');
-        const confirmDeleteProcedureBtn = document.getElementById('confirm-delete-procedure-btn');
-        const deleteProcedureForm = document.getElementById('delete-procedure-form'); 
-        const closeButtons = deleteProcedureModalEl.querySelectorAll('.modal-close-btn');
-        let currentProcedureIdToDelete = null;
-
-        function openModal() {
-            deleteProcedureModalEl.classList.remove('hidden');
-            // Trigger transitions
-            setTimeout(() => {
-                modalBackdrop.classList.add('opacity-100');
-                modalContent.classList.add('opacity-100', 'scale-100');
-                modalContent.classList.remove('scale-95'); // Start slightly scaled down
-            }, 10); // Small delay for transitions
-        }
-
-        function closeModal() {
-            modalBackdrop.classList.remove('opacity-100');
-            modalContent.classList.remove('opacity-100', 'scale-100');
-            modalContent.classList.add('scale-95'); 
-            // Wait for transitions to finish before hiding
-            setTimeout(() => {
-                 deleteProcedureModalEl.classList.add('hidden');
-            }, 300); // Match transition duration
-            currentProcedureIdToDelete = null; // Reset ID when closing
-        }
-
-        deleteProcedureButtons.forEach(button => {
-            button.addEventListener('click', function() {
-                currentProcedureIdToDelete = this.getAttribute('data-proc-id');
-                const procedureInfo = this.getAttribute('data-proc-info');
-                
-                if (procedureInfoPlaceholder) {
-                    procedureInfoPlaceholder.textContent = procedureInfo; // Update modal body
-                }
-                
-                // Update form action 
-                if (deleteProcedureForm && currentProcedureIdToDelete) {
-                     const deleteUrl = `{{ url_for('dietitian.delete_procedure', procedure_id=0) }}`.replace('/0', '/' + currentProcedureIdToDelete);
-                     deleteProcedureForm.action = deleteUrl;
-                     console.log("Delete form action set to:", deleteUrl);
+        
+        // *** START: Auto-open Assign Modal Logic (Now placed AFTER instance creation) ***
+        if ((window.location.hash === '#assign-dietitian' || new URLSearchParams(window.location.search).get('action') === 'assign') && assignDietitianModalInstance) {
+            console.log("[Auto Open Modal] Assign action detected AND modal instance exists.");
+            // Cuộn trang lên đầu
+            window.scrollTo(0, 0);
+            showChoiceView(); // Reset to choice view
+            
+            // Update title and action based on current patient
+            const assignPatientNameEl = document.querySelector('#assignDietitianModal #modal-title');
+            const pagePatientName = document.querySelector('h2.text-2xl.font-bold')?.textContent.trim();
+            if (assignPatientNameEl) {
+                if(pagePatientName) {
+                   assignPatientNameEl.textContent = `Assign Dietitian for ${pagePatientName}`;
                 } else {
-                    console.error("Could not set delete form action.");
+                   assignPatientNameEl.textContent = `Assign Dietitian`; // Fallback
                 }
-                
-                openModal();
-            });
-        });
+            }
+            const patientIdMatch = window.location.pathname.match(/\/patients\/([^\/]+)/);
+            const currentPatientId = patientIdMatch ? patientIdMatch[1] : null;
+            if (assignForm && currentPatientId) {
+                assignForm.action = `/patients/${currentPatientId}/assign_dietitian`;
+                console.log("[Auto Open Modal] Updated form action to: ", assignForm.action);
+            } else {
+                 console.warn("[Auto Open Modal] Could not find assign form or patient ID to update action.");
+            }
+            
+            // Show the modal
+            assignDietitianModalInstance.show();
+            console.log("[Auto Open Modal] Modal show() called.");
 
-        // Add click listener for the final confirm button
-        if (confirmDeleteProcedureBtn && deleteProcedureForm) {
-            // No event listener needed here, the form submission handles it.
-            // The button type="submit" inside the form works.
-        }
-        
-        // Add listeners to close buttons
-        closeButtons.forEach(button => {
-            button.addEventListener('click', closeModal);
-        });
+            // Remove the query parameter and hash after opening
+            if (history.replaceState) {
+                const url = window.location.pathname;
+                history.replaceState(null, null, url);
+                console.log("[Auto Open Modal] Removed query parameters/hash from URL.");
+            }
 
-        // Close modal on backdrop click
-        modalBackdrop.addEventListener('click', closeModal); 
+        } else if ((window.location.hash === '#assign-dietitian' || new URLSearchParams(window.location.search).get('action') === 'assign') && !assignDietitianModalInstance) {
+             console.error("[Auto Open Modal] Assign action detected, but modal instance is NOT available.");
+        }
+        // *** END: Auto-open Assign Modal Logic ***
 
-        // *** END: Logic for Delete Procedure Modal ***
+        // Add event listeners for delete procedure buttons (if on this page)
+        // ... (existing delete procedure logic) ...
 
     });
 </script>
 
 {% endblock %}
-    
\ No newline at end of file
diff --git a/app/templates/patients.html b/app/templates/patients.html
index 14cfff0b03b0638caf0e80eb2034e25c8786d5da..71d5eede16cca9eb9a1a50c8231d10ae33285814 100644
--- a/app/templates/patients.html
+++ b/app/templates/patients.html
@@ -34,6 +34,13 @@
                     <option value="COMPLETED">Completed</option>
                 </select>
                 
+                {# Nút Bulk Auto Assign (Admin only) #}
+                {% if current_user.is_admin %}
+                <button type="button" id="bulkAssignBtn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500">
+                    <i class="fas fa-users-cog mr-2"></i> Bulk Auto Assign
+                </button>
+                {% endif %}
+
                 <button type="button" id="addPatientBtn" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                     <i class="fas fa-plus mr-2"></i> Add Patient
                 </button>
@@ -63,7 +70,7 @@
                             Age/Gender
                         </th>
                         <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
-                            Status
+                            Status & Actions
                         </th>
                         <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                             Admission Date
@@ -95,19 +102,33 @@
                             <div class="text-sm text-gray-900">{{ patient.age }} years</div>
                             <div class="text-sm text-gray-500">{{ patient.gender }}</div>
                         </td>
-                        <td class="px-6 py-4 whitespace-nowrap">
-                            <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full 
-                                {% set p_status = patient.status %}
-                                {% set p_color_map = {
-                                    'NOT_ASSESSED': 'gray',
-                                    'NEEDS_ASSESSMENT': 'amber',
-                                    'ASSESSMENT_IN_PROGRESS': 'blue',
-                                    'COMPLETED': 'green'
-                                } %}
-                                {% set p_color = p_color_map.get(p_status.value if p_status else 'UNKNOWN', 'gray') %}
-                                bg-{{ p_color }}-100 text-{{ p_color }}-800" data-status="{{ patient.status.value if patient.status else 'UNKNOWN' }}">
-                                {{ patient.status.value.replace('_', ' ').title() if patient.status else 'Unknown' }}
+                        <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500" data-status="{{ patient.status.value if patient.status else 'UNKNOWN' }}">
+                            {% set p_status = patient.status %}
+                            {% set p_color_map = {
+                                PatientStatus.NOT_ASSESSED: 'gray',
+                                PatientStatus.NEEDS_ASSESSMENT: 'red',
+                                PatientStatus.ASSESSMENT_IN_PROGRESS: 'blue',
+                                PatientStatus.COMPLETED: 'green'
+                            } %}
+                            {% set p_color = p_color_map.get(p_status, 'gray') %}
+                            <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-{{ p_color }}-100 text-{{ p_color }}-800 mr-2">
+                                <span class="w-2 h-2 mr-1.5 rounded-full bg-{{ p_color }}-500"></span>
+                                {{ p_status.value.replace('_', ' ').title() if p_status else 'Unknown' }}
                             </span>
+
+                            {# Restore Assign button for Needs Assessment #}
+                            {% if p_status == PatientStatus.NEEDS_ASSESSMENT %}
+                                <a href="{{ url_for('patients.patient_detail', patient_id=patient.patient_id) }}?action=assign"
+                                   class="inline-flex items-center px-2 py-1 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
+                                   title="Assign Dietitian">
+                                    <i class="fas fa-user-plus mr-1"></i> Assign
+                                </a>
+                            {# Only show dietitian name to Admins #}
+                            {% elif p_status == PatientStatus.ASSESSMENT_IN_PROGRESS and patient.assigned_dietitian %}
+                                {% if current_user.is_admin %}
+                                    <span class="text-xs text-gray-600 italic">by {{ patient.assigned_dietitian.full_name }}</span>
+                                {% endif %}
+                            {% endif %}
                         </td>
                         <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ patient.admission_date.strftime('%Y-%m-%d') if patient.admission_date else 'N/A' }}</td>
                         <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
@@ -214,6 +235,44 @@
         </div>
     </div>
 </div>
+
+<!-- Bulk Assign Confirmation Modal -->
+<div id="bulk-assign-modal" class="hidden fixed z-10 inset-0 overflow-y-auto" aria-labelledby="bulk-assign-title" role="dialog" aria-modal="true">
+    <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
+        <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
+        <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
+        <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
+            <form id="bulk-assign-form" action="{{ url_for('patients.bulk_auto_assign') }}" method="POST">
+                {{ EmptyForm().csrf_token }}
+                <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
+                    <div class="sm:flex sm:items-start">
+                        <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-purple-100 sm:mx-0 sm:h-10 sm:w-10">
+                            <i class="fas fa-question-circle text-purple-600"></i>
+                        </div>
+                        <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
+                            <h3 class="text-lg leading-6 font-medium text-gray-900" id="bulk-assign-title">
+                                Confirm Bulk Auto Assignment
+                            </h3>
+                            <div class="mt-2">
+                                <p class="text-sm text-gray-500">
+                                    Are you sure that you want to AUTO-ASSIGN every patient with 'Needs Assessment' status?
+                                </p>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
+                    <button type="submit" id="confirm-bulk-assign" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-purple-600 text-base font-medium text-white hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 sm:ml-3 sm:w-auto sm:text-sm">
+                        Confirm Auto Assign
+                    </button>
+                    <button type="button" id="cancel-bulk-assign" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
+                        Cancel
+                    </button>
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
 {% endblock %}
 
 {% block scripts %}
@@ -333,8 +392,106 @@
             console.warn('Delete modal elements not found. Delete functionality might be incomplete.');
         }
 
-        // Initial filter on load (optional)
-        // filterAndSearchPatients(); 
+        // --- Bulk Assign Modal Logic --- 
+        const bulkAssignBtn = document.getElementById('bulkAssignBtn');
+        const bulkAssignModal = document.getElementById('bulk-assign-modal');
+        const confirmBulkAssignBtn = document.getElementById('confirm-bulk-assign');
+        const cancelBulkAssignBtn = document.getElementById('cancel-bulk-assign');
+
+        if (bulkAssignBtn && bulkAssignModal && confirmBulkAssignBtn && cancelBulkAssignBtn) {
+            bulkAssignBtn.addEventListener('click', function() {
+                bulkAssignModal.classList.remove('hidden');
+                bulkAssignModal.setAttribute('aria-hidden', 'false');
+            });
+
+            cancelBulkAssignBtn.addEventListener('click', function() {
+                bulkAssignModal.classList.add('hidden');
+                bulkAssignModal.setAttribute('aria-hidden', 'true');
+            });
+
+            // Đóng modal khi click bên ngoài
+            bulkAssignModal.addEventListener('click', function(event) {
+                 if (event.target === bulkAssignModal) {
+                     cancelBulkAssignBtn.click(); 
+                 }
+            });
+
+            confirmBulkAssignBtn.addEventListener('click', function(event) {
+                event.preventDefault(); // Ngăn submit ngay lập tức để hiển thị loading
+                console.log("Bulk assign confirmed. Submitting form...");
+                // Hiển thị loading spinner hoặc vô hiệu hóa nút
+                confirmBulkAssignBtn.disabled = true;
+                confirmBulkAssignBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Processing...';
+
+                // Tìm form thật trong modal
+                const form = document.getElementById('bulk-assign-form'); 
+                
+                if (form) {
+                    // Submit form thật
+                    form.submit();
+                } else {
+                    console.error("Bulk Assign Form (#bulk-assign-form) not found!");
+                    alert("Error: Could not submit the assignment request.");
+                    // Khôi phục trạng thái nút nếu form không tìm thấy
+                    confirmBulkAssignBtn.disabled = false;
+                    confirmBulkAssignBtn.innerHTML = 'Confirm Auto Assign';
+                }
+
+                // Không cần ẩn modal ở đây, trang sẽ reload sau khi submit
+            });
+        } else {
+            if (!bulkAssignBtn) console.log("Bulk Assign button not found (might be non-admin user).");
+            // Các kiểm tra khác nếu cần
+        }
+        
+        // --- Assign Dietitian Modal Trigger (from patient list) --- 
+        // --- BỎ PHẦN NÀY ĐI VÌ NÚT ASSIGN GIỜ LÀ LINK NAVIGATE --- 
+        /*
+        const assignModalTriggers = document.querySelectorAll('.open-assign-modal');
+        const assignDietitianModalEl = document.getElementById('assignDietitianModal'); // ID của modal gán (cần tạo ở base/macro)
+        let assignDietitianModalInstance = null; 
+        const assignDietitianForm = document.getElementById('assignDietitianForm'); // ID của form trong modal
+        const assignPatientNameEl = document.querySelector('#assignDietitianModal #modal-title'); // Element để hiển thị tên BN
+
+        if (assignDietitianModalEl && typeof bootstrap !== 'undefined') {
+            assignDietitianModalInstance = new bootstrap.Modal(assignDietitianModalEl);
+        } else {
+            console.warn("Assign Dietitian modal element or Bootstrap JS not found. Assign buttons on list page will not work.");
+        }
+
+        if (assignModalTriggers.length > 0 && assignDietitianModalInstance && assignDietitianForm && assignPatientNameEl) {
+            assignModalTriggers.forEach(button => {
+                button.addEventListener('click', function() {
+                    const patientId = this.dataset.patientId;
+                    const patientName = this.dataset.patientName;
+                    
+                    // Cập nhật action của form trong modal
+                    assignDietitianForm.action = `{{ url_for('patients.assign_dietitian', patient_id=0) }}`.replace('/0', '/' + patientId);
+                    
+                    // Cập nhật tiêu đề modal (tùy chọn)
+                    assignPatientNameEl.textContent = `Assign Dietitian for ${patientName}`;
+                    
+                    // Reset về view lựa chọn ban đầu khi mở modal
+                    const choiceView = document.getElementById('assignmentChoiceView');
+                    const manualView = document.getElementById('manualAssignmentView');
+                    const backBtn = document.getElementById('backToChoiceBtn');
+                    const submitBtn = document.getElementById('assignSubmitBtn');
+                    if (choiceView) choiceView.style.display = 'grid';
+                    if (manualView) manualView.style.display = 'none';
+                    if (backBtn) backBtn.classList.add('hidden');
+                    if (submitBtn) submitBtn.classList.add('hidden'); // Ẩn nút submit ban đầu
+                    
+                    // Mở modal
+                    assignDietitianModalInstance.show();
+                });
+            });
+        } else {
+            if (assignModalTriggers.length > 0) {
+                console.warn("Assign Dietitian modal/form/title elements missing. Assign buttons might not work correctly.");
+            }
+        }
+        */
+
     });
 </script>
 {% endblock %}
diff --git a/app/templates/profile.html b/app/templates/profile.html
index 2a71f6ee28bac5bbc21e7d339e6168edc9c0a5f2..56bbb6ee58ddf1999f48a121706d872c528612b6 100644
--- a/app/templates/profile.html
+++ b/app/templates/profile.html
@@ -1,10 +1,20 @@
 {% extends "base.html" %}
+{% from "_macros.html" import status_badge %}
 
 {% block title %}Hồ sơ người dùng{% endblock %}
 
 {% block content %}
 <div class="container mx-auto mt-10 px-4">
-    <h1 class="text-3xl font-semibold text-gray-800 mb-6">Hồ sơ người dùng</h1>
+    <div class="flex justify-between items-center mb-6">
+        <h1 class="text-3xl font-semibold text-gray-800">Hồ sơ người dùng</h1>
+        {# Display Dietitian Status (Read-only) #}
+        {% if current_user.role == 'Dietitian' and current_user.dietitian %}
+            <div class="flex items-center space-x-2">
+                <span class="text-sm font-medium text-gray-600">Trạng thái:</span>
+                {{ status_badge(current_user.dietitian.status.value) }}
+            </div>
+        {% endif %}
+    </div>
 
     <div class="bg-white shadow-md rounded-lg overflow-hidden">
         <div class="px-6 py-4">
@@ -28,14 +38,36 @@
                         <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.email }}</dd>
                     </div>
                     <div class="bg-gray-50 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                        <dt class="text-sm font-medium text-gray-500">Số điện thoại</dt>
+                        <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.phone or 'Chưa cập nhật' }}</dd>
+                    </div>
+                    <div class="bg-white px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                         <dt class="text-sm font-medium text-gray-500">Vai trò</dt>
                         <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.role }}</dd>
                     </div>
-                    <div class="bg-white px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                    {# Dietitian Specific Info #}
+                    {% if current_user.role == 'Dietitian' and current_user.dietitian %}
+                         <div class="bg-gray-50 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                            <dt class="text-sm font-medium text-gray-500">Dietitian ID</dt>
+                            <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.dietitian.formattedID }}</dd>
+                        </div>
+                         <div class="bg-white px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                            <dt class="text-sm font-medium text-gray-500">Chuyên môn</dt>
+                            <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.dietitian.specialization or 'Chưa cập nhật' }}</dd>
+                        </div>
+                         <div class="bg-gray-50 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                            <dt class="text-sm font-medium text-gray-500">Số bệnh nhân đang theo dõi</dt>
+                            <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.dietitian.patient_count }}</dd>
+                        </div>
+                        <div class="bg-white px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
+                            <dt class="text-sm font-medium text-gray-500">Ghi chú</dt>
+                            <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2 whitespace-pre-wrap">{{ current_user.dietitian.notes or 'Không có ghi chú' }}</dd>
+                        </div>
+                    {% endif %}
+                    <div class="bg-gray-50 px-4 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                         <dt class="text-sm font-medium text-gray-500">Ngày tham gia</dt>
                         <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ current_user.created_at.strftime('%d/%m/%Y') if current_user.created_at else 'N/A' }}</dd>
                     </div>
-                    <!-- Add more fields as needed -->
                 </dl>
             </div>
         </div>
diff --git a/app/templates/report.html b/app/templates/report.html
index f9cfca4c225d711e62b48c2cd5178d7a19fbdaf4..e7e27a547c47fe7dcccfa629bf82800554a82234 100644
--- a/app/templates/report.html
+++ b/app/templates/report.html
@@ -156,9 +156,10 @@
                                 'Pending': 'yellow',
                                 'Completed': 'green'
                             } %}
-                            {% set status_color = status_color_map.get(report.status, 'gray') %}
+                            {# Directly use report.status.value, assuming it exists #}
+                            {% set status_color = status_color_map.get(report.status.value, 'gray') %}
                             <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-{{ status_color }}-100 text-{{ status_color }}-800">
-                                {{ report.status }}
+                                {{ report.status.value }}
                                 </span>
                         </td>
                         <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
@@ -168,13 +169,15 @@
                                 </a>
                                 {# Nút Edit chỉ hiện khi Draft/Pending và có quyền (Admin hoặc Author/Assigned Dietitian) #}
                                 {# Logic kiểm tra quyền đã phức tạp hơn, dựa vào route xử lý #}
-                                {% if report.status in ['Draft', 'Pending'] %}
+                                {# Compare status value directly #}
+                                {% if report.status.value in ['Draft', 'Pending'] %}
                                 <a href="{{ url_for('report.edit_report', report_id=report.id) }}" class="text-indigo-600 hover:text-indigo-900 transform hover:scale-110 transition-transform duration-200" title="Edit Report">
                                     <i class="fas fa-edit"></i>
                                 </a>
                                 {% endif %}
                                 {# Nút Download chỉ khi Completed #}
-                                {% if report.status == 'Completed' %}
+                                {# Compare status value directly #}
+                                {% if report.status.value == 'Completed' %}
                                 <a href="{{ url_for('report.download_report', report_id=report.id) }}" class="text-green-600 hover:text-green-900 transform hover:scale-110 transition-transform duration-200" title="Download PDF">
                                     <i class="fas fa-file-pdf"></i>
                                 </a>
@@ -277,7 +280,7 @@
                 <div class="flex items-center">
                     <div class="flex-shrink-0 flex items-center justify-center h-12 w-12 bg-gray-100 rounded-md">
                         <i class="fas fa-pencil-alt text-gray-600"></i>
-                    </div>
+                                </div>
                     <div class="ml-5 w-0 flex-1">
                         <dl>
                             <dt class="text-sm font-medium text-gray-500 truncate">Draft Reports</dt>
diff --git a/app/templates/report_form.html b/app/templates/report_form.html
index adbf97be2e5b0ea593d6d857f48b512511c46989..93f1406df28f06c796a397d0a05a08ac4fdc12e0 100644
--- a/app/templates/report_form.html
+++ b/app/templates/report_form.html
@@ -344,7 +344,9 @@
                 </button>
                 
                 {# Nút Hoàn thành - Chỉ hiện khi status là Pending và user có quyền #}
-                {% if report and report.status in ['Draft', 'Pending'] and (current_user.is_admin or current_user.userID == report.author_id or (report.patient and current_user.userID == report.patient.assigned_dietitian_user_id)) %}
+                {# DEBUG: Print status value #}
+                <!-- DEBUG: Report Status Value: {{ report.status.value if report.status else 'None' }} --> 
+                {% if report and (report.status.value if report.status else None) in ['Draft', 'Pending'] and (current_user.is_admin or current_user.userID == report.author_id or (report.patient and current_user.userID == report.patient.assigned_dietitian_user_id)) %}
                 <button type="submit" name="action" value="complete" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-5 rounded-md transition duration-300 shadow hover:shadow-md">
                      <i class="fas fa-check-circle mr-2"></i> Complete Report
                 </button>
diff --git a/app/templates/upload.html b/app/templates/upload.html
index 0a18294f9e9684c6dc58b7a56e48af76f596daa7..c6b4c46c89096d20ef9ab45459b46e557ab1726e 100644
--- a/app/templates/upload.html
+++ b/app/templates/upload.html
@@ -163,8 +163,8 @@
                     </div>
                     
                     <div class="mt-6">
-                        <a href="{{ url_for('static', filename='templates/initial_patient_data_sample.csv') }}" class="text-primary-600 hover:text-primary-500 text-sm font-medium flex items-center">
-                            <i class="fas fa-download mr-1"></i> Tải xuống mẫu CSV (Bệnh nhân ban đầu)
+                        <a href="{{ url_for('upload.download_template', template_type='new_patients') }}" class="text-primary-600 hover:text-primary-500 text-sm font-medium flex items-center">
+                            <i class="fas fa-download mr-1"></i> Tải xuống mẫu CSV (Bệnh nhân mới)
                         </a>
                     </div>
                 </div>
diff --git a/app/utils/csv_handler.py b/app/utils/csv_handler.py
index aebabbc4b4d31887e4b6edb403c3c988f062bcff..54e1117c76060bd2663231fa3b0dab4c548b6575 100644
--- a/app/utils/csv_handler.py
+++ b/app/utils/csv_handler.py
@@ -5,7 +5,7 @@ import json
 from flask import current_app
 from datetime import datetime
 from app import db
-from app.models.patient import Patient
+from app.models.patient import Patient, PatientStatus
 from app.models.encounter import Encounter
 from app.models.measurement import PhysiologicalMeasurement
 from app.models.referral import Referral
@@ -81,115 +81,236 @@ def process_new_patients_csv(uploaded_file_id):
     """
     Process a CSV file containing only new patient information.
     Automatically generates a new patientID for each row.
+    Checks for duplicates based on firstName, lastName, gender, blood_type.
     Expected headers: firstName, lastName, age, gender, height, weight, blood_type
+    Returns a dictionary with processing results including duplicate checks.
     """
     uploaded_file = UploadedFile.query.get(uploaded_file_id)
     if not uploaded_file:
-        return {'success': False, 'error': f'Uploaded file record not found (ID: {uploaded_file_id})'}
+        return {
+            'success': False, 
+            'error': f'Uploaded file record not found (ID: {uploaded_file_id})',
+            'processed_records': 0, 'error_records': 0, 'total_records': 0,
+            'duplicate_errors': [], 'format_errors': False
+        }
 
     total_records = 0
     processed_records = 0
     error_records = 0
-    errors = []
+    errors = [] # For detailed logging
+    duplicate_names = [] # Store names of duplicate patients
+    format_error_found = False # Flag for format errors
     patient_ids_created = []
 
+    # Sử dụng header này để kiểm tra và lấy dữ liệu
     expected_headers = ['firstName', 'lastName', 'age', 'gender', 'height', 'weight', 'blood_type']
+    # Các trường dùng để kiểm tra trùng lặp
+    duplicate_check_fields = ['firstName', 'lastName', 'gender', 'blood_type'] 
 
     try:
         with open(uploaded_file.filePath, 'r', encoding=uploaded_file.file_encoding, newline='') as csvfile:
             reader = csv.DictReader(csvfile, delimiter=uploaded_file.delimiter)
+            actual_headers = reader.fieldnames
 
-            # Basic header check
-            if not reader.fieldnames or any(h not in reader.fieldnames for h in expected_headers):
-                missing = [h for h in expected_headers if h not in (reader.fieldnames or [])]
-                raise ValueError(f"CSV header mismatch. Missing or incorrect columns: {', '.join(missing)}. Required: {', '.join(expected_headers)}")
+            # --- Header Check --- 
+            if not actual_headers:
+                format_error_found = True
+                raise ValueError("CSV file is empty or header row is missing.")
+            
+            missing_headers = [h for h in expected_headers if h not in actual_headers]
+            if missing_headers:
+                format_error_found = True
+                raise ValueError(f"CSV header mismatch. Missing columns: {', '.join(missing_headers)}.")
+            # --- End Header Check --- 
 
             for i, row in enumerate(reader):
                 total_records += 1
                 row_num = i + 2 # Row number in the original file
+                
+                # Lấy dữ liệu cần thiết cho kiểm tra trùng lặp và tạo bệnh nhân
+                first_name = row.get('firstName', '').strip()
+                last_name = row.get('lastName', '').strip()
+                gender = row.get('gender', '').strip().lower() or None
+                blood_type = row.get('blood_type', '').strip() or None
+                age_str = row.get('age')
+                height_str = row.get('height')
+                weight_str = row.get('weight')
+                
+                # --- Validate required fields for duplicate check and basic info --- 
+                if not first_name or not last_name or not gender:
+                    error_records += 1
+                    errors.append({'row': row_num, 'error': 'Missing required field(s): firstName, lastName, or gender.', 'data': dict(row)})
+                    format_error_found = True # Thiếu trường cơ bản coi là lỗi format
+                    continue # Bỏ qua dòng này
+                # --- End Validation --- 
 
                 try:
-                    # Generate new Patient ID
+                    # --- Duplicate Check --- 
+                    existing_patient = Patient.query.filter_by(
+                        firstName=first_name,
+                        lastName=last_name,
+                        gender=gender,
+                        # Kiểm tra cả blood_type nếu nó có giá trị
+                        blood_type=blood_type if blood_type else None 
+                    ).first()
+
+                    if existing_patient:
+                        error_records += 1
+                        full_name = f"{first_name} {last_name}"
+                        if full_name not in duplicate_names: # Chỉ thêm tên 1 lần
+                            duplicate_names.append(full_name)
+                        errors.append({'row': row_num, 'error': f'Duplicate patient found: {full_name} (Gender: {gender}, Blood: {blood_type})', 'data': dict(row)})
+                        continue # Bỏ qua, không tạo bệnh nhân trùng
+                    # --- End Duplicate Check --- 
+                    
+                    # Generate new Patient ID if not duplicate
                     new_patient_id = _get_next_patient_id()
-                    # Check if somehow this ID already exists (extremely unlikely but safe)
                     while Patient.query.get(new_patient_id):
                          current_app.logger.warning(f"Generated patient ID {new_patient_id} already exists. Regenerating.")
-                         new_patient_id = _get_next_patient_id() # Regenerate
+                         new_patient_id = _get_next_patient_id()
 
-                    height = _parse_float(row.get('height'))
-                    weight = _parse_float(row.get('weight'))
+                    # Parse remaining data (potential format errors here)
+                    age = _parse_int(age_str)
+                    height = _parse_float(height_str)
+                    weight = _parse_float(weight_str)
+                    
+                    # Kiểm tra lỗi parse cơ bản
+                    parse_errors = []
+                    if age is None and age_str: parse_errors.append(f"Invalid age value: '{age_str}'")
+                    if height is None and height_str: parse_errors.append(f"Invalid height value: '{height_str}'")
+                    if weight is None and weight_str: parse_errors.append(f"Invalid weight value: '{weight_str}'")
+                    
+                    if parse_errors:
+                        format_error_found = True
+                        raise ValueError("; ".join(parse_errors)) # Gộp lỗi parse
 
+                    # Create Patient object
                     new_patient = Patient(
-                        id=new_patient_id, # Use generated ID
-                        firstName=row.get('firstName'),
-                        lastName=row.get('lastName'),
-                        age=_parse_int(row.get('age')),
-                        gender=row.get('gender', '').lower() or None,
+                        id=new_patient_id,
+                        firstName=first_name,
+                        lastName=last_name,
+                        age=age,
+                        gender=gender,
                         height=height,
                         weight=weight,
-                        blood_type=row.get('blood_type'),
-                        admission_date=datetime.utcnow(), # Set admission date on creation
-                        status='Active' # Default status
+                        blood_type=blood_type,
+                        admission_date=datetime.utcnow(),
+                        status=PatientStatus.NOT_ASSESSED # Sử dụng giá trị Enum mặc định 
                     )
-                    new_patient.calculate_bmi() # Calculate BMI if possible
+                    new_patient.calculate_bmi()
 
                     db.session.add(new_patient)
-                    db.session.commit() # Commit each patient
+                    db.session.commit() # Commit từng bệnh nhân để lấy ID tiếp theo chính xác
                     processed_records += 1
                     patient_ids_created.append(new_patient_id)
 
                 except IntegrityError as ie:
                     db.session.rollback()
                     error_records += 1
-                    errors.append({'row': row_num, 'error': f'Database integrity error (check logs): {str(ie)}', 'data': dict(row)})
+                    # Lỗi này thường nghiêm trọng, có thể là lỗi database
+                    errors.append({'row': row_num, 'error': f'Database integrity error: {str(ie)}', 'data': dict(row)})
                     current_app.logger.error(f"IntegrityError on row {row_num} for file {uploaded_file_id}: {ie}", exc_info=True)
+                    # Không chắc đây là lỗi format, có thể là lỗi DB
+                except ValueError as ve: # Bắt lỗi từ parse hoặc kiểm tra thiếu trường
+                     db.session.rollback()
+                     error_records += 1
+                     errors.append({'row': row_num, 'error': f'Data format error: {str(ve)}', 'data': dict(row)})
+                     format_error_found = True # Đánh dấu có lỗi format
                 except Exception as e:
                     db.session.rollback()
                     error_records += 1
-                    error_message = f'Error processing row: {str(e)}'
+                    error_message = f'Unexpected error processing row: {str(e)}'
                     errors.append({'row': row_num, 'error': error_message, 'data': dict(row)})
                     current_app.logger.error(f"CSV Processing Error (Row {row_num}, File {uploaded_file_id}): {error_message}", exc_info=True)
+                    # Lỗi không mong đợi cũng có thể coi là format nếu không rõ
+                    # format_error_found = True 
 
-    except Exception as e:
-        db.session.rollback() # Rollback any potential partial commits if file reading fails
+    except ValueError as file_ve: # Bắt lỗi từ kiểm tra header hoặc đọc file
+        db.session.rollback()
         uploaded_file.status = 'failed'
-        error_message = f'Failed to read or process CSV file: {str(e)}'
-        uploaded_file.error_details = json.dumps([{'row': 0, 'error': error_message}]) # Store file-level error
+        error_message = f'Failed to process CSV file: {str(file_ve)}'
+        uploaded_file.error_details = json.dumps([{'row': 0, 'error': error_message}])
         current_app.logger.error(f"CSV File Processing Failed (File ID: {uploaded_file_id}): {error_message}", exc_info=True)
         db.session.commit()
-        return {'success': False, 'error': error_message}
+        return {
+            'success': False, 'error': error_message, 
+            'processed_records': 0, 'error_records': total_records, 'total_records': total_records, 
+            'duplicate_errors': [], 'format_errors': True # Lỗi header/đọc file là lỗi format
+        }
+    except Exception as file_e:
+        db.session.rollback()
+        uploaded_file.status = 'failed'
+        error_message = f'Failed to read or process CSV file: {str(file_e)}'
+        uploaded_file.error_details = json.dumps([{'row': 0, 'error': error_message}])
+        current_app.logger.error(f"CSV File Processing Failed (File ID: {uploaded_file_id}): {error_message}", exc_info=True)
+        db.session.commit()
+        return {
+            'success': False, 'error': error_message, 
+            'processed_records': 0, 'error_records': total_records, 'total_records': total_records,
+            'duplicate_errors': [], 'format_errors': True # Lỗi đọc file cũng coi là format
+        }
 
-    # Update uploaded file record
+    # --- Final Update and Return --- 
     uploaded_file.total_records = total_records
     uploaded_file.processed_records = processed_records
     uploaded_file.error_records = error_records
-    if errors:
-        # Limit the size of error details stored in DB
-        MAX_ERROR_DETAIL_LENGTH = 16000000 # Slightly less than 16MB to be safe
-        error_details_json = json.dumps(errors)
-        if len(error_details_json) > MAX_ERROR_DETAIL_LENGTH:
-             # Provide a summary instead of truncated data
-            summary_error = {
-                 "summary": f"Too many errors ({len(errors)}) to store details.",
-                 "message": "Check application logs for full error details.",
-                 "first_few_errors": errors[:5] # Store first few errors as sample
-             }
-            uploaded_file.error_details = json.dumps(summary_error)
-            current_app.logger.warning(f"Error details for file {uploaded_file_id} exceeded length limit. Storing summary.")
-        else:
-             uploaded_file.error_details = error_details_json
-
-    uploaded_file.status = 'completed_with_errors' if error_records > 0 else 'completed'
-    uploaded_file.process_end = datetime.utcnow() # Mark end time
+    # ... (Logic giới hạn kích thước error_details giữ nguyên) ...
+    MAX_ERROR_DETAIL_LENGTH = 16000000 
+    error_details_json = json.dumps(errors)
+    if len(error_details_json) > MAX_ERROR_DETAIL_LENGTH:
+        summary_error = {
+            "summary": f"Too many errors ({len(errors)}) to store details.",
+            "message": "Check application logs for full error details.",
+            "first_few_errors": errors[:5] 
+        }
+        uploaded_file.error_details = json.dumps(summary_error)
+        current_app.logger.warning(f"Error details for file {uploaded_file_id} exceeded length limit. Storing summary.")
+    else:
+        uploaded_file.error_details = error_details_json
+
+    # Xác định trạng thái cuối cùng và success flag
+    final_success_flag = False
+    if error_records == 0: # Không có lỗi gì cả
+        uploaded_file.status = 'completed'
+        final_success_flag = True
+    elif processed_records > 0 or len(duplicate_names) > 0: # Có xử lý thành công hoặc chỉ có lỗi trùng lặp
+        if format_error_found: # Nếu có cả lỗi format thì vẫn là failed
+            uploaded_file.status = 'failed'
+            final_success_flag = False
+        else: # Chỉ có lỗi trùng lặp hoặc lỗi khác không phải format
+            uploaded_file.status = 'completed_with_errors'
+            final_success_flag = True # Coi là success vì file đọc được, chỉ là data trùng
+    else: # Không xử lý được gì và có lỗi (thường là format hoặc lỗi nghiêm trọng)
+        uploaded_file.status = 'failed'
+        final_success_flag = False
+        
+    uploaded_file.process_end = datetime.utcnow()
 
     try:
         db.session.commit()
     except Exception as commit_error:
          current_app.logger.error(f"Failed to commit final status for uploaded file {uploaded_file_id}: {commit_error}", exc_info=True)
-         # Even if final commit fails, processing might have partially succeeded.
-         return {'success': error_records == 0, 'processed_records': processed_records, 'error_records': error_records, 'total_records': total_records, 'errors': errors}
-
-    return {'success': error_records == 0, 'processed_records': processed_records, 'error_records': error_records, 'total_records': total_records, 'errors': errors, 'patient_ids_created': patient_ids_created}
+         # Return kết quả đã tính toán được ngay cả khi commit cuối lỗi
+         return {
+            'success': final_success_flag, 
+            'processed_records': processed_records, 
+            'error_records': error_records, 
+            'total_records': total_records, 
+            'duplicate_errors': duplicate_names, 
+            'format_errors': format_error_found,
+            'error_details': errors # Trả về lỗi chi tiết nếu cần
+        }
+
+    return {
+        'success': final_success_flag, 
+        'processed_records': processed_records, 
+        'error_records': error_records, 
+        'total_records': total_records,
+        'duplicate_errors': duplicate_names, 
+        'format_errors': format_error_found,
+        'patient_ids_created': patient_ids_created,
+        'error_details': errors # Trả về lỗi chi tiết nếu cần
+    }
 
 
 # --- Renamed old process_csv function ---
@@ -480,8 +601,6 @@ def process_encounter_measurements_csv(file_stream, patient_id_param, encounter_
              # Điều này không nên xảy ra nếu route hoạt động đúng, nhưng kiểm tra cho chắc
              raise ValueError(f"Không tìm thấy encounter hợp lệ với ID {encounter_id_param} cho bệnh nhân {patient_id_param}.")
         target_custom_encounter_id = current_encounter.custom_encounter_id
-        if not target_custom_encounter_id:
-             raise ValueError(f"Encounter ID {encounter_id_param} không có custom_encounter_id được đặt.")
 
         for row in reader:
             row_count += 1
@@ -500,9 +619,10 @@ def process_encounter_measurements_csv(file_stream, patient_id_param, encounter_
                 if not csv_patient_id:
                     errors.append(f"Dòng {row_count + 1}: Thiếu giá trị PatientID.")
                     continue # Skip this row
-                if not csv_encounter_id_str:
-                    errors.append(f"Dòng {row_count + 1}: Thiếu giá trị EncounterID.")
-                    continue # Skip this row
+                # Bỏ kiểm tra thiếu EncounterID ở đây vì logic so sánh sẽ xử lý nó
+                # if not csv_encounter_id_str:
+                #    errors.append(f"Dòng {row_count + 1}: Thiếu giá trị EncounterID.")
+                #    continue # Skip this row
 
                 # Check if PatientID matches the parameter
                 if csv_patient_id != str(patient_id_param):
@@ -512,11 +632,19 @@ def process_encounter_measurements_csv(file_stream, patient_id_param, encounter_
                     continue # Skip this row
 
                 # Check if EncounterID from CSV matches the custom_id of the current encounter
-                if csv_encounter_id_str != target_custom_encounter_id:
-                    skipped_mismatch_count += 1
-                    # Log nếu cần
-                    # current_app.logger.info(f"CSV Dòng {row_count + 1}: Bỏ qua do EncounterID ({csv_encounter_id_str}) không khớp với encounter hiện tại ({target_custom_encounter_id}).")
-                    continue # Skip this row
+                # Chỉ thực hiện so sánh nếu target_custom_encounter_id tồn tại
+                if target_custom_encounter_id:
+                     if csv_encounter_id_str != target_custom_encounter_id:
+                         skipped_mismatch_count += 1
+                         # Log nếu cần
+                         # current_app.logger.info(f"CSV Dòng {row_count + 1}: Bỏ qua do EncounterID ({csv_encounter_id_str}) không khớp với encounter hiện tại ({target_custom_encounter_id}).")
+                         continue # Skip this row
+                else:
+                     # Nếu encounter đích không có custom_id, bỏ qua dòng này vì không thể so sánh
+                     skipped_mismatch_count += 1
+                     # Log nếu cần
+                     # current_app.logger.warning(f"CSV Dòng {row_count + 1}: Bỏ qua do Encounter đích ({encounter_id_param}) không có custom_encounter_id để so sánh.")
+                     continue # Skip this row
 
                 # --- Data Parsing and Type Conversion ---
                 # Gán patient_id và encounter_id từ parameter URL (vì dòng này đã được xác định là của encounter này)
diff --git a/app/utils/decorators.py b/app/utils/decorators.py
index 9c128964e575ea1eef6c28289678d17d15e73d67..c5788e8b024bf2d74d53effae9fcbf6b1a60493e 100644
--- a/app/utils/decorators.py
+++ b/app/utils/decorators.py
@@ -1,5 +1,5 @@
 from functools import wraps
-from flask import flash, redirect, url_for, request
+from flask import flash, redirect, url_for, request, abort
 from flask_login import current_user
 
 def admin_required(f):
@@ -13,4 +13,28 @@ def admin_required(f):
             flash('Bạn không có quyền truy cập trang này.', 'danger')
             return redirect(url_for('dashboard.index')) # Hoặc trang chính khác
         return f(*args, **kwargs)
-    return decorated_function 
\ No newline at end of file
+    return decorated_function
+
+def permission_required(*roles):
+    """
+    Decorator để giới hạn quyền truy cập cho các vai trò cụ thể.
+    Kiểm tra xem vai trò của current_user có nằm trong danh sách roles được phép không.
+    """
+    def decorator(f):
+        @wraps(f)
+        def decorated_function(*args, **kwargs):
+            if not current_user.is_authenticated:
+                flash('Bạn cần đăng nhập để truy cập trang này.', 'warning')
+                return redirect(url_for('auth.login', next=request.url))
+            
+            # Chuyển đổi các role thành chữ hoa để so sánh không phân biệt chữ hoa/thường
+            allowed_roles = [role.upper() for role in roles]
+            user_role = current_user.role.upper() if hasattr(current_user, 'role') and current_user.role else None
+            
+            if user_role not in allowed_roles:
+                # flash(f'Bạn không có quyền truy cập trang này. Yêu cầu quyền: {", ".join(roles)}', 'danger')
+                # return redirect(url_for('dashboard.index')) # Hoặc trang phù hợp
+                abort(403) # Trả về lỗi 403 Forbidden thay vì flash và redirect
+            return f(*args, **kwargs)
+        return decorated_function
+    return decorator 
\ No newline at end of file
diff --git a/app/utils/notifications_helper.py b/app/utils/notifications_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..92c28eb179544436c6845e6e4e94bf153f73c77a
--- /dev/null
+++ b/app/utils/notifications_helper.py
@@ -0,0 +1,63 @@
+from app import db
+from app.models.notification import Notification
+from app.models.user import User
+from flask import url_for, current_app
+
+def create_notification(recipient_user_id, message, link=None):
+    """
+    Tạo và lưu một thông báo mới cho một người dùng cụ thể.
+
+    Args:
+        recipient_user_id: ID của người dùng sẽ nhận thông báo.
+        message: Nội dung thông báo.
+        link: URL tùy chọn để người dùng nhấp vào (sẽ được tạo bằng url_for nếu là tuple).
+    """
+    try:
+        # Kiểm tra xem recipient_user_id có hợp lệ không
+        recipient = User.query.get(recipient_user_id)
+        if not recipient:
+            current_app.logger.warning(f"Attempted to create notification for non-existent user ID: {recipient_user_id}")
+            return
+
+        # Xử lý link nếu nó là tuple (endpoint, **kwargs)
+        final_link = link
+        if isinstance(link, tuple) and len(link) > 0:
+            endpoint = link[0]
+            kwargs = link[1] if len(link) > 1 else {}
+            try:
+                # Luôn tạo link _external=True để đảm bảo hoạt động đúng trong mọi ngữ cảnh (ví dụ: email)
+                final_link = url_for(endpoint, **kwargs, _external=True)
+            except Exception as e:
+                current_app.logger.error(f"Failed to build URL for notification: endpoint={endpoint}, kwargs={kwargs}, error={e}", exc_info=True)
+                final_link = None # Không đặt link nếu không tạo được
+
+        notification = Notification(
+            user_id=recipient_user_id,
+            message=message,
+            link=final_link
+        )
+        db.session.add(notification)
+        # Không commit ở đây, để commit diễn ra trong route gốc
+        # Nhưng flush để có thể lấy ID nếu cần ngay lập tức (hiếm khi cần cho helper này)
+        # db.session.flush() 
+        current_app.logger.info(f"Prepared notification for user {recipient_user_id}: '{message[:50]}...' Link: {final_link}")
+
+    except Exception as e:
+        current_app.logger.error(f"Error creating notification for user {recipient_user_id}: {e}", exc_info=True)
+        # Không rollback ở đây vì có thể ảnh hưởng đến transaction gốc
+
+def create_notification_for_admins(message, link=None, exclude_user_id=None):
+    """
+    Tạo thông báo cho tất cả Admin.
+
+    Args:
+        message: Nội dung thông báo.
+        link: URL tùy chọn hoặc tuple (endpoint, **kwargs).
+        exclude_user_id: ID của người dùng (thường là người thực hiện hành động) để loại trừ.
+    """
+    admins = User.query.filter_by(role='Admin').all()
+    for admin in admins:
+        if admin.userID != exclude_user_id:
+            create_notification(admin.userID, message, link)
+
+# Có thể thêm các hàm helper khác nếu cần, ví dụ: create_notification_for_role(...) 
\ No newline at end of file
diff --git a/migrations/versions/0a3ef5c08275_add_encounterstatus_enum_and_update_.py b/migrations/versions/0a3ef5c08275_add_encounterstatus_enum_and_update_.py
deleted file mode 100644
index 02c0469e690b5e7da375e5d0f4b87d7f4be61a5c..0000000000000000000000000000000000000000
--- a/migrations/versions/0a3ef5c08275_add_encounterstatus_enum_and_update_.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""Add EncounterStatus enum and update status column
-
-Revision ID: 0a3ef5c08275
-Revises: 6d1cbb67e2e2
-Create Date: 2025-04-19 11:22:10.500929
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '0a3ef5c08275'
-down_revision = '6d1cbb67e2e2'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.alter_column('status',
-               existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=50),
-               type_=sa.Enum('ACTIVE', 'NEEDS_ASSESSMENT', 'ASSESSMENT_IN_PROGRESS', 'AWAITING_REVIEW', 'COMPLETED', 'CANCELLED', name='encounterstatus'),
-               existing_nullable=False)
-        batch_op.create_index(batch_op.f('ix_encounters_status'), ['status'], unique=False)
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_encounters_status'))
-        batch_op.alter_column('status',
-               existing_type=sa.Enum('ACTIVE', 'NEEDS_ASSESSMENT', 'ASSESSMENT_IN_PROGRESS', 'AWAITING_REVIEW', 'COMPLETED', 'CANCELLED', name='encounterstatus'),
-               type_=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=50),
-               existing_nullable=False)
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/11d6fcd3a14d_refactor_encounter_and_measurement_.py b/migrations/versions/11d6fcd3a14d_refactor_encounter_and_measurement_.py
deleted file mode 100644
index eca7ecbccbb641849874cc23b639bceeb161a0bb..0000000000000000000000000000000000000000
--- a/migrations/versions/11d6fcd3a14d_refactor_encounter_and_measurement_.py
+++ /dev/null
@@ -1,229 +0,0 @@
-"""Refactor Encounter and Measurement models with relationships
-
-Revision ID: 11d6fcd3a14d
-Revises: 
-Create Date: 2025-04-17 13:48:23.589559
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = '11d6fcd3a14d'
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.create_table('users',
-    sa.Column('userID', sa.Integer(), nullable=False),
-    sa.Column('email', sa.String(length=100), nullable=False),
-    sa.Column('password_hash', sa.String(length=128), nullable=True),
-    sa.Column('firstName', sa.String(length=50), nullable=False),
-    sa.Column('lastName', sa.String(length=50), nullable=False),
-    sa.Column('role', sa.Enum('Admin', 'Dietitian', name='user_roles_new'), nullable=False),
-    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
-    sa.Column('last_login', sa.DateTime(timezone=True), nullable=True),
-    sa.PrimaryKeyConstraint('userID'),
-    sa.UniqueConstraint('email')
-    )
-    op.create_table('activity_logs',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('user_id', sa.Integer(), nullable=False),
-    sa.Column('action', sa.String(length=255), nullable=False),
-    sa.Column('details', sa.Text(), nullable=True),
-    sa.Column('timestamp', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['user_id'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('dietitians',
-    sa.Column('dietitianID', sa.Integer(), nullable=False),
-    sa.Column('user_id', sa.Integer(), nullable=True),
-    sa.Column('firstName', sa.String(length=50), nullable=False),
-    sa.Column('lastName', sa.String(length=50), nullable=False),
-    sa.Column('status', sa.Enum('AVAILABLE', 'UNAVAILABLE', 'ON_LEAVE', name='dietitianstatus'), nullable=False),
-    sa.Column('email', sa.String(length=100), nullable=True),
-    sa.Column('phone', sa.String(length=20), nullable=True),
-    sa.Column('specialization', sa.String(length=100), nullable=True),
-    sa.Column('notes', sa.Text(), nullable=True),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['user_id'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('dietitianID'),
-    sa.UniqueConstraint('email'),
-    sa.UniqueConstraint('user_id')
-    )
-    op.create_table('uploadedfiles',
-    sa.Column('fileID', sa.Integer(), nullable=False),
-    sa.Column('fileName', sa.String(length=255), nullable=False),
-    sa.Column('filePath', sa.String(length=255), nullable=False),
-    sa.Column('userID', sa.Integer(), nullable=False),
-    sa.Column('original_filename', sa.String(length=256), nullable=False),
-    sa.Column('file_size', sa.Integer(), nullable=True),
-    sa.Column('file_type', sa.String(length=64), nullable=True),
-    sa.Column('delimiter', sa.String(length=10), nullable=True),
-    sa.Column('file_encoding', sa.String(length=20), nullable=True),
-    sa.Column('status', sa.String(length=50), nullable=True),
-    sa.Column('process_start', sa.DateTime(), nullable=True),
-    sa.Column('process_end', sa.DateTime(), nullable=True),
-    sa.Column('total_records', sa.Integer(), nullable=True),
-    sa.Column('processed_records', sa.Integer(), nullable=True),
-    sa.Column('error_records', sa.Integer(), nullable=True),
-    sa.Column('error_details', sa.Text(length=16777215), nullable=True),
-    sa.Column('process_referrals', sa.Boolean(), nullable=True),
-    sa.Column('description', sa.Text(), nullable=True),
-    sa.Column('notes', sa.Text(), nullable=True),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.Column('upload_date', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['userID'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('fileID')
-    )
-    op.create_table('patients',
-    sa.Column('patientID', sa.String(length=20), nullable=False),
-    sa.Column('firstName', sa.String(length=50), nullable=False),
-    sa.Column('lastName', sa.String(length=50), nullable=False),
-    sa.Column('age', sa.Integer(), nullable=False),
-    sa.Column('gender', sa.Enum('male', 'female', 'other', name='gender_types'), nullable=False),
-    sa.Column('bmi', sa.Float(), nullable=True),
-    sa.Column('status', sa.String(length=20), nullable=True),
-    sa.Column('height', sa.Float(), nullable=True),
-    sa.Column('weight', sa.Float(), nullable=True),
-    sa.Column('blood_type', sa.String(length=10), nullable=True),
-    sa.Column('admission_date', sa.DateTime(), nullable=True),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.Column('dietitian_id', sa.Integer(), nullable=True),
-    sa.ForeignKeyConstraint(['dietitian_id'], ['dietitians.dietitianID'], ),
-    sa.PrimaryKeyConstraint('patientID')
-    )
-    op.create_table('encounters',
-    sa.Column('encounterID', sa.Integer(), nullable=False),
-    sa.Column('patient_id', sa.String(length=20), nullable=False),
-    sa.Column('notes', sa.Text(), nullable=True),
-    sa.Column('start_time', sa.DateTime(), nullable=False),
-    sa.Column('dietitian_id', sa.Integer(), nullable=True),
-    sa.Column('status', sa.String(length=50), nullable=False),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['dietitian_id'], ['users.userID'], ),
-    sa.ForeignKeyConstraint(['patient_id'], ['patients.patientID'], ),
-    sa.PrimaryKeyConstraint('encounterID')
-    )
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.create_index(batch_op.f('ix_encounters_start_time'), ['start_time'], unique=False)
-
-    op.create_table('reports',
-    sa.Column('reportID', sa.Integer(), nullable=False),
-    sa.Column('userID', sa.Integer(), nullable=False),
-    sa.Column('patientID', sa.String(length=20), nullable=False),
-    sa.Column('reportDateTime', sa.DateTime(), nullable=False),
-    sa.Column('reportTitle', sa.String(length=100), nullable=False),
-    sa.Column('reportContent', sa.Text(), nullable=True),
-    sa.Column('status', sa.String(length=20), nullable=True),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['patientID'], ['patients.patientID'], ),
-    sa.ForeignKeyConstraint(['userID'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('reportID')
-    )
-    op.create_table('physiologicalmeasurements',
-    sa.Column('measurementID', sa.Integer(), nullable=False),
-    sa.Column('encounterID', sa.Integer(), nullable=False),
-    sa.Column('patientID', sa.String(length=20), nullable=False),
-    sa.Column('measurementDateTime', sa.DateTime(), nullable=False),
-    sa.Column('end_tidal_co2', sa.Float(), nullable=True),
-    sa.Column('feed_vol', sa.Float(), nullable=True),
-    sa.Column('feed_vol_adm', sa.Float(), nullable=True),
-    sa.Column('fio2', sa.Float(), nullable=True),
-    sa.Column('fio2_ratio', sa.Float(), nullable=True),
-    sa.Column('insp_time', sa.Float(), nullable=True),
-    sa.Column('oxygen_flow_rate', sa.Float(), nullable=True),
-    sa.Column('peep', sa.Float(), nullable=True),
-    sa.Column('pip', sa.Float(), nullable=True),
-    sa.Column('resp_rate', sa.Float(), nullable=True),
-    sa.Column('sip', sa.Float(), nullable=True),
-    sa.Column('tidal_vol', sa.Float(), nullable=True),
-    sa.Column('tidal_vol_actual', sa.Float(), nullable=True),
-    sa.Column('tidal_vol_kg', sa.Float(), nullable=True),
-    sa.Column('tidal_vol_spon', sa.Float(), nullable=True),
-    sa.Column('temperature', sa.Float(), nullable=True),
-    sa.Column('heart_rate', sa.Integer(), nullable=True),
-    sa.Column('respiratory_rate', sa.Integer(), nullable=True),
-    sa.Column('blood_pressure_systolic', sa.Integer(), nullable=True),
-    sa.Column('blood_pressure_diastolic', sa.Integer(), nullable=True),
-    sa.Column('oxygen_saturation', sa.Float(), nullable=True),
-    sa.Column('bmi', sa.Float(), nullable=True),
-    sa.Column('tidal_volume', sa.Float(), nullable=True),
-    sa.Column('notes', sa.Text(), nullable=True),
-    sa.Column('referral_score', sa.Float(), nullable=True, comment='Score from ML algorithm indicating referral recommendation (0-1)'),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['encounterID'], ['encounters.encounterID'], ),
-    sa.ForeignKeyConstraint(['patientID'], ['patients.patientID'], ),
-    sa.PrimaryKeyConstraint('measurementID')
-    )
-    with op.batch_alter_table('physiologicalmeasurements', schema=None) as batch_op:
-        batch_op.create_index(batch_op.f('ix_physiologicalmeasurements_encounterID'), ['encounterID'], unique=False)
-        batch_op.create_index(batch_op.f('ix_physiologicalmeasurements_measurementDateTime'), ['measurementDateTime'], unique=False)
-
-    op.create_table('procedures',
-    sa.Column('procedureID', sa.Integer(), nullable=False),
-    sa.Column('encounterID', sa.Integer(), nullable=False),
-    sa.Column('patientID', sa.String(length=20), nullable=False),
-    sa.Column('procedureType', sa.String(length=100), nullable=False),
-    sa.Column('procedureName', sa.String(length=255), nullable=True),
-    sa.Column('procedureDateTime', sa.DateTime(), nullable=False),
-    sa.Column('procedureEndDateTime', sa.DateTime(), nullable=True),
-    sa.Column('procedureResults', sa.Text(), nullable=True),
-    sa.Column('description', sa.Text(), nullable=True),
-    sa.Column('created_at', sa.DateTime(), nullable=True),
-    sa.Column('updated_at', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['encounterID'], ['encounters.encounterID'], ),
-    sa.ForeignKeyConstraint(['patientID'], ['patients.patientID'], ),
-    sa.PrimaryKeyConstraint('procedureID')
-    )
-    op.create_table('referrals',
-    sa.Column('referralID', sa.Integer(), nullable=False),
-    sa.Column('encounterID', sa.Integer(), nullable=False),
-    sa.Column('patientID', sa.String(length=20), nullable=False),
-    sa.Column('is_ml_recommended', sa.Boolean(), nullable=True),
-    sa.Column('is_staff_referred', sa.Boolean(), nullable=True),
-    sa.Column('referral_status', sa.Enum('Not Needed', 'ML Recommended', 'Pending Review', 'Staff Referred', 'Completed', 'Rejected'), nullable=True),
-    sa.Column('referralRequestedDateTime', sa.DateTime(), nullable=True),
-    sa.Column('referralCompletedDateTime', sa.DateTime(), nullable=True),
-    sa.Column('dietitianID', sa.Integer(), nullable=True),
-    sa.Column('notes', sa.Text(), nullable=True),
-    sa.Column('createdAt', sa.DateTime(), nullable=True),
-    sa.Column('updatedAt', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['dietitianID'], ['dietitians.dietitianID'], ),
-    sa.ForeignKeyConstraint(['encounterID'], ['encounters.encounterID'], ),
-    sa.ForeignKeyConstraint(['patientID'], ['patients.patientID'], ),
-    sa.PrimaryKeyConstraint('referralID')
-    )
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.drop_table('referrals')
-    op.drop_table('procedures')
-    with op.batch_alter_table('physiologicalmeasurements', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_physiologicalmeasurements_measurementDateTime'))
-        batch_op.drop_index(batch_op.f('ix_physiologicalmeasurements_encounterID'))
-
-    op.drop_table('physiologicalmeasurements')
-    op.drop_table('reports')
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_encounters_start_time'))
-
-    op.drop_table('encounters')
-    op.drop_table('patients')
-    op.drop_table('uploadedfiles')
-    op.drop_table('dietitians')
-    op.drop_table('activity_logs')
-    op.drop_table('users')
-    # ### end Alembic commands ###
diff --git a/migrations/versions/1af2bf32a740_add_noti.py b/migrations/versions/1af2bf32a740_add_noti.py
deleted file mode 100644
index 3c19b2ec7b1527eafe6c7ddb5b739aaa811aef2d..0000000000000000000000000000000000000000
--- a/migrations/versions/1af2bf32a740_add_noti.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""add noti
-
-Revision ID: 1af2bf32a740
-Revises: 8ea1605c5393
-Create Date: 2025-04-17 23:29:42.398550
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '1af2bf32a740'
-down_revision = '8ea1605c5393'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.create_table('notifications',
-    sa.Column('id', mysql.INTEGER(unsigned=True), nullable=False),
-    sa.Column('user_id', sa.Integer(), nullable=False),
-    sa.Column('message', sa.Text(), nullable=False),
-    sa.Column('timestamp', sa.DateTime(), nullable=True),
-    sa.Column('is_read', sa.Boolean(), nullable=False),
-    sa.Column('link', sa.String(length=255), nullable=True),
-    sa.ForeignKeyConstraint(['user_id'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    with op.batch_alter_table('notifications', schema=None) as batch_op:
-        batch_op.create_index(batch_op.f('ix_notifications_timestamp'), ['timestamp'], unique=False)
-        batch_op.create_index(batch_op.f('ix_notifications_user_id'), ['user_id'], unique=False)
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('notifications', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_notifications_user_id'))
-        batch_op.drop_index(batch_op.f('ix_notifications_timestamp'))
-
-    op.drop_table('notifications')
-    # ### end Alembic commands ###
diff --git a/migrations/versions/375b7f062a5f_add_ass_date_to_patient.py b/migrations/versions/375b7f062a5f_add_ass_date_to_patient.py
deleted file mode 100644
index d9414503dedacbf345ffd1289c284b3b4428750d..0000000000000000000000000000000000000000
--- a/migrations/versions/375b7f062a5f_add_ass_date_to_patient.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""add ass date to patient
-
-Revision ID: 375b7f062a5f
-Revises: 0a3ef5c08275
-Create Date: 2025-04-19 11:58:03.391271
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '375b7f062a5f'
-down_revision = '0a3ef5c08275'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('patients', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('assignment_date', sa.DateTime(), nullable=True))
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('patients', schema=None) as batch_op:
-        batch_op.drop_column('assignment_date')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/617d526bbcee_add.py b/migrations/versions/617d526bbcee_add.py
deleted file mode 100644
index c34079c2c3a94f8aeb5a769c964f904469ce445a..0000000000000000000000000000000000000000
--- a/migrations/versions/617d526bbcee_add.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""add
-
-Revision ID: 617d526bbcee
-Revises: 9d82acfed8fa
-Create Date: 2025-04-18 22:06:25.939688
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '617d526bbcee'
-down_revision = '9d82acfed8fa'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('end_time', sa.DateTime(), nullable=True))
-        batch_op.add_column(sa.Column('encounter_type', sa.String(length=50), nullable=True))
-        batch_op.add_column(sa.Column('needs_intervention', sa.Boolean(), nullable=False))
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.drop_column('needs_intervention')
-        batch_op.drop_column('encounter_type')
-        batch_op.drop_column('end_time')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/6d1cbb67e2e2_assign.py b/migrations/versions/6d1cbb67e2e2_assign.py
deleted file mode 100644
index 3a5c81d9de2dfd3cefa8ff43565afe37f3ad2166..0000000000000000000000000000000000000000
--- a/migrations/versions/6d1cbb67e2e2_assign.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""assign
-
-Revision ID: 6d1cbb67e2e2
-Revises: bfff5e42bd2f
-Create Date: 2025-04-19 00:03:59.435077
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '6d1cbb67e2e2'
-down_revision = 'bfff5e42bd2f'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('patients', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('assigned_dietitian_user_id', sa.Integer(), nullable=True))
-        batch_op.drop_constraint('patients_ibfk_1', type_='foreignkey')
-        batch_op.create_foreign_key(None, 'users', ['assigned_dietitian_user_id'], ['userID'])
-        batch_op.drop_column('dietitian_id')
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('patients', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('dietitian_id', mysql.INTEGER(), autoincrement=False, nullable=True))
-        batch_op.drop_constraint(None, type_='foreignkey')
-        batch_op.create_foreign_key('patients_ibfk_1', 'dietitians', ['dietitian_id'], ['dietitianID'])
-        batch_op.drop_column('assigned_dietitian_user_id')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/734f404fad77_add_encounter_id_and_dietitian_id_to_.py b/migrations/versions/734f404fad77_add_encounter_id_and_dietitian_id_to_.py
deleted file mode 100644
index a725f86a49510a496c1234e9d10ab298ac504d9c..0000000000000000000000000000000000000000
--- a/migrations/versions/734f404fad77_add_encounter_id_and_dietitian_id_to_.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""Add encounter_id and dietitian_id to Report mode
-
-Revision ID: 734f404fad77
-Revises: cf323f4343be
-Create Date: 2025-04-19 15:30:30.321092
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '734f404fad77'
-down_revision = 'cf323f4343be'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('reports', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('encounterID', sa.Integer(), nullable=True))
-        batch_op.add_column(sa.Column('dietitianID', sa.Integer(), nullable=True))
-        batch_op.add_column(sa.Column('completed_date', sa.DateTime(), nullable=True))
-        batch_op.create_foreign_key(None, 'encounters', ['encounterID'], ['encounterID'])
-        batch_op.create_foreign_key(None, 'users', ['dietitianID'], ['userID'])
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('reports', schema=None) as batch_op:
-        batch_op.drop_constraint(None, type_='foreignkey')
-        batch_op.drop_constraint(None, type_='foreignkey')
-        batch_op.drop_column('completed_date')
-        batch_op.drop_column('dietitianID')
-        batch_op.drop_column('encounterID')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/8116b7d4aede_initital.py b/migrations/versions/8116b7d4aede_initital.py
new file mode 100644
index 0000000000000000000000000000000000000000..f61f1161e3acc787855f8a8c75743c2745a0ae97
--- /dev/null
+++ b/migrations/versions/8116b7d4aede_initital.py
@@ -0,0 +1,103 @@
+"""initital
+
+Revision ID: 8116b7d4aede
+Revises: 
+Create Date: 2025-04-21 13:04:43.442726
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import mysql
+
+# revision identifiers, used by Alembic.
+revision = '8116b7d4aede'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    with op.batch_alter_table('support_message_read_status', schema=None) as batch_op:
+        # Drop constraints FIRST
+        try: # Use try-except in case constraints don't exist or have different names
+            batch_op.drop_constraint('support_message_read_status_ibfk_1', type_='foreignkey')
+        except Exception as e:
+            print(f"Info: Could not drop FK support_message_read_status_ibfk_1 (might not exist): {e}")
+        try:
+            batch_op.drop_constraint('support_message_read_status_ibfk_2', type_='foreignkey')
+        except Exception as e:
+            print(f"Info: Could not drop FK support_message_read_status_ibfk_2 (might not exist): {e}")
+
+        # Now drop the index
+        try:
+            batch_op.drop_index('uq_user_message_read')
+        except Exception as e:
+            print(f"Info: Could not drop Index uq_user_message_read (might not exist): {e}")
+
+    # Drop the table after constraints and indexes are gone
+    try:
+        op.drop_table('support_message_read_status')
+    except Exception as e:
+        print(f"Info: Could not drop table support_message_read_status (might not exist): {e}")
+    with op.batch_alter_table('reports', schema=None) as batch_op:
+        batch_op.alter_column('status',
+               existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=20),
+               type_=sa.Enum('DRAFT', 'PENDING', 'COMPLETED', 'CANCELLED', name='reportstatus'),
+               nullable=False)
+
+    with op.batch_alter_table('support_messages', schema=None) as batch_op:
+        batch_op.alter_column('timestamp',
+               existing_type=mysql.DATETIME(),
+               nullable=False)
+
+    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
+        batch_op.alter_column('error_details',
+               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
+               type_=sa.Text(length=16777215),
+               existing_nullable=True)
+
+    with op.batch_alter_table('users', schema=None) as batch_op:
+        batch_op.add_column(sa.Column('last_support_visit', sa.DateTime(timezone=True), nullable=True))
+
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    with op.batch_alter_table('users', schema=None) as batch_op:
+        batch_op.drop_column('last_support_visit')
+
+    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
+        batch_op.alter_column('error_details',
+               existing_type=sa.Text(length=16777215),
+               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
+               existing_nullable=True)
+
+    with op.batch_alter_table('support_messages', schema=None) as batch_op:
+        batch_op.alter_column('timestamp',
+               existing_type=mysql.DATETIME(),
+               nullable=True)
+
+    with op.batch_alter_table('reports', schema=None) as batch_op:
+        batch_op.alter_column('status',
+               existing_type=sa.Enum('DRAFT', 'PENDING', 'COMPLETED', 'CANCELLED', name='reportstatus'),
+               type_=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=20),
+               nullable=True)
+
+    op.create_table('support_message_read_status',
+    sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False),
+    sa.Column('user_id', mysql.INTEGER(), autoincrement=False, nullable=False),
+    sa.Column('message_id', mysql.INTEGER(), autoincrement=False, nullable=False),
+    sa.Column('read_at', mysql.DATETIME(), nullable=True),
+    sa.ForeignKeyConstraint(['message_id'], ['support_messages.id'], name='support_message_read_status_ibfk_1'),
+    sa.ForeignKeyConstraint(['user_id'], ['users.userID'], name='support_message_read_status_ibfk_2'),
+    sa.PrimaryKeyConstraint('id'),
+    mysql_collate='utf8mb4_unicode_ci',
+    mysql_default_charset='utf8mb4',
+    mysql_engine='InnoDB'
+    )
+    with op.batch_alter_table('support_message_read_status', schema=None) as batch_op:
+        batch_op.create_index('uq_user_message_read', ['user_id', 'message_id'], unique=True)
+
+    # ### end Alembic commands ###
diff --git a/migrations/versions/8ea1605c5393_add_encounter_id.py b/migrations/versions/8ea1605c5393_add_encounter_id.py
deleted file mode 100644
index 119aacb0c51654f7703624b6ad6f01eddbd0eec2..0000000000000000000000000000000000000000
--- a/migrations/versions/8ea1605c5393_add_encounter_id.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""add encounter_id
-
-Revision ID: 8ea1605c5393
-Revises: 11d6fcd3a14d
-Create Date: 2025-04-17 22:35:02.844489
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '8ea1605c5393'
-down_revision = '11d6fcd3a14d'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('custom_encounter_id', sa.String(length=50), nullable=True))
-        batch_op.create_index(batch_op.f('ix_encounters_custom_encounter_id'), ['custom_encounter_id'], unique=True)
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_encounters_custom_encounter_id'))
-        batch_op.drop_column('custom_encounter_id')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/9d82acfed8fa_update_report.py b/migrations/versions/9d82acfed8fa_update_report.py
deleted file mode 100644
index ccf807f304f6facb9e3a51eff12ad7a00865b268..0000000000000000000000000000000000000000
--- a/migrations/versions/9d82acfed8fa_update_report.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""update report
-
-Revision ID: 9d82acfed8fa
-Revises: 1af2bf32a740
-Create Date: 2025-04-18 11:06:04.115320
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = '9d82acfed8fa'
-down_revision = '1af2bf32a740'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('reports', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('authorID', sa.Integer(), nullable=False))
-        batch_op.add_column(sa.Column('referralID', sa.Integer(), nullable=True))
-        batch_op.add_column(sa.Column('report_date', sa.DateTime(), nullable=False))
-        batch_op.add_column(sa.Column('report_type', sa.String(length=50), nullable=False))
-        batch_op.add_column(sa.Column('nutritional_status', sa.String(length=100), nullable=True))
-        batch_op.add_column(sa.Column('nutrition_diagnosis', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('assessment_details', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('nutritional_risk_score', sa.Float(), nullable=True))
-        batch_op.add_column(sa.Column('malnutrition_screening_result', sa.String(length=50), nullable=True))
-        batch_op.add_column(sa.Column('intervention_plan', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('intervention_summary', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('dietary_recommendations', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('supplement_recommendations', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('enteral_feeding_recommendations', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('parenteral_nutrition_recommendations', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('monitoring_plan', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('follow_up_needed', sa.Boolean(), nullable=True))
-        batch_op.add_column(sa.Column('follow_up_date', sa.Date(), nullable=True))
-        batch_op.add_column(sa.Column('notes', sa.Text(), nullable=True))
-        batch_op.add_column(sa.Column('finalized_at', sa.DateTime(), nullable=True))
-        batch_op.drop_constraint('reports_ibfk_1', type_='foreignkey')
-        batch_op.create_foreign_key(None, 'referrals', ['referralID'], ['referralID'])
-        batch_op.create_foreign_key(None, 'users', ['authorID'], ['userID'])
-        batch_op.drop_column('reportTitle')
-        batch_op.drop_column('reportContent')
-        batch_op.drop_column('reportDateTime')
-        batch_op.drop_column('userID')
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('reports', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('userID', mysql.INTEGER(), autoincrement=False, nullable=False))
-        batch_op.add_column(sa.Column('reportDateTime', mysql.DATETIME(), nullable=False))
-        batch_op.add_column(sa.Column('reportContent', mysql.TEXT(collation='utf8mb4_unicode_ci'), nullable=True))
-        batch_op.add_column(sa.Column('reportTitle', mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=100), nullable=False))
-        batch_op.drop_constraint(None, type_='foreignkey')
-        batch_op.drop_constraint(None, type_='foreignkey')
-        batch_op.create_foreign_key('reports_ibfk_1', 'users', ['userID'], ['userID'])
-        batch_op.drop_column('finalized_at')
-        batch_op.drop_column('notes')
-        batch_op.drop_column('follow_up_date')
-        batch_op.drop_column('follow_up_needed')
-        batch_op.drop_column('monitoring_plan')
-        batch_op.drop_column('parenteral_nutrition_recommendations')
-        batch_op.drop_column('enteral_feeding_recommendations')
-        batch_op.drop_column('supplement_recommendations')
-        batch_op.drop_column('dietary_recommendations')
-        batch_op.drop_column('intervention_summary')
-        batch_op.drop_column('intervention_plan')
-        batch_op.drop_column('malnutrition_screening_result')
-        batch_op.drop_column('nutritional_risk_score')
-        batch_op.drop_column('assessment_details')
-        batch_op.drop_column('nutrition_diagnosis')
-        batch_op.drop_column('nutritional_status')
-        batch_op.drop_column('report_type')
-        batch_op.drop_column('report_date')
-        batch_op.drop_column('referralID')
-        batch_op.drop_column('authorID')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/af3981c4f7e5_add_chat.py b/migrations/versions/af3981c4f7e5_add_chat.py
deleted file mode 100644
index 690999d6537b1d2ff1b457b2189d7aac83de0889..0000000000000000000000000000000000000000
--- a/migrations/versions/af3981c4f7e5_add_chat.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""add chat
-
-Revision ID: af3981c4f7e5
-Revises: eaf7e2cfc895
-Create Date: 2025-04-20 19:25:34.946616
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = 'af3981c4f7e5'
-down_revision = 'eaf7e2cfc895'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.create_table('support_messages',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('sender_id', sa.Integer(), nullable=False),
-    sa.Column('content', sa.Text(), nullable=False),
-    sa.Column('timestamp', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['sender_id'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('id')
-    )
-    op.create_table('support_message_read_status',
-    sa.Column('id', sa.Integer(), nullable=False),
-    sa.Column('user_id', sa.Integer(), nullable=False),
-    sa.Column('message_id', sa.Integer(), nullable=False),
-    sa.Column('read_at', sa.DateTime(), nullable=True),
-    sa.ForeignKeyConstraint(['message_id'], ['support_messages.id'], ),
-    sa.ForeignKeyConstraint(['user_id'], ['users.userID'], ),
-    sa.PrimaryKeyConstraint('id'),
-    sa.UniqueConstraint('user_id', 'message_id', name='uq_user_message_read')
-    )
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    op.drop_table('support_message_read_status')
-    op.drop_table('support_messages')
-    # ### end Alembic commands ###
diff --git a/migrations/versions/bfff5e42bd2f_allow_null_for_needs_intervention.py b/migrations/versions/bfff5e42bd2f_allow_null_for_needs_intervention.py
deleted file mode 100644
index bb21b71136921d364830f1964bf5f3f23a0d9be6..0000000000000000000000000000000000000000
--- a/migrations/versions/bfff5e42bd2f_allow_null_for_needs_intervention.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""Allow null for needs_intervention
-
-Revision ID: bfff5e42bd2f
-Revises: e631bdcdce5f
-Create Date: 2025-04-18 23:01:08.603279
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = 'bfff5e42bd2f'
-down_revision = 'e631bdcdce5f'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.alter_column('needs_intervention',
-               existing_type=mysql.TINYINT(display_width=1),
-               nullable=True)
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.alter_column('needs_intervention',
-               existing_type=mysql.TINYINT(display_width=1),
-               nullable=False)
-
-    # ### end Alembic commands ###
diff --git "a/migrations/versions/cf323f4343be_thay_\304\221\341\273\225i_tr\341\272\241ng_th\303\241i.py" "b/migrations/versions/cf323f4343be_thay_\304\221\341\273\225i_tr\341\272\241ng_th\303\241i.py"
deleted file mode 100644
index ce366055b4bf4c6a54921e69e2b570136bfaf0f8..0000000000000000000000000000000000000000
--- "a/migrations/versions/cf323f4343be_thay_\304\221\341\273\225i_tr\341\272\241ng_th\303\241i.py"
+++ /dev/null
@@ -1,38 +0,0 @@
-"""thay đổi trạng thái
-
-Revision ID: cf323f4343be
-Revises: fd2ac04819c9
-Create Date: 2025-04-19 14:39:22.579139
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = 'cf323f4343be'
-down_revision = 'fd2ac04819c9'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/e631bdcdce5f_ml.py b/migrations/versions/e631bdcdce5f_ml.py
deleted file mode 100644
index 42785d6a089d27848dfaab7d56f06e447df20b97..0000000000000000000000000000000000000000
--- a/migrations/versions/e631bdcdce5f_ml.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""ml
-
-Revision ID: e631bdcdce5f
-Revises: 617d526bbcee
-Create Date: 2025-04-18 22:18:57.110317
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = 'e631bdcdce5f'
-down_revision = '617d526bbcee'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('diagnosis', sa.String(length=255), nullable=True))
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('encounters', schema=None) as batch_op:
-        batch_op.drop_column('diagnosis')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/eaf7e2cfc895_add_procedure.py b/migrations/versions/eaf7e2cfc895_add_procedure.py
deleted file mode 100644
index e63dcdd27ee7cd05eeb21b3d200e8d1fd077518d..0000000000000000000000000000000000000000
--- a/migrations/versions/eaf7e2cfc895_add_procedure.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""add procedure
-
-Revision ID: eaf7e2cfc895
-Revises: 734f404fad77
-Create Date: 2025-04-19 17:41:56.281693
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = 'eaf7e2cfc895'
-down_revision = '734f404fad77'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('reports', schema=None) as batch_op:
-        batch_op.add_column(sa.Column('related_procedure_id', sa.Integer(), nullable=True))
-        batch_op.create_foreign_key(None, 'procedures', ['related_procedure_id'], ['procedureID'])
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('reports', schema=None) as batch_op:
-        batch_op.drop_constraint(None, type_='foreignkey')
-        batch_op.drop_column('related_procedure_id')
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/fd2ac04819c9_update_status_enums_and_referral_.py b/migrations/versions/fd2ac04819c9_update_status_enums_and_referral_.py
deleted file mode 100644
index 3da25157938a3344a96bb8b1c47b0803d68da0bf..0000000000000000000000000000000000000000
--- a/migrations/versions/fd2ac04819c9_update_status_enums_and_referral_.py
+++ /dev/null
@@ -1,136 +0,0 @@
-"""Update status enums and referral dietitian relationship
-
-Revision ID: fd2ac04819c9
-Revises: 375b7f062a5f
-Create Date: 2025-04-19 13:54:15.128405
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects import mysql
-
-# revision identifiers, used by Alembic.
-revision = 'fd2ac04819c9'
-down_revision = '375b7f062a5f'
-branch_labels = None
-depends_on = None
-
-# Định nghĩa Enum để sử dụng trong migration
-patient_status_enum = sa.Enum('NOT_ASSESSED', 'NEEDS_ASSESSMENT', 'ASSESSMENT_IN_PROGRESS', 'COMPLETED', name='patientstatus')
-referral_status_enum = sa.Enum('DIETITIAN_UNASSIGNED', 'WAITING_FOR_REPORT', 'COMPLETED', name='referralstatus')
-old_referral_status_enum_for_downgrade = sa.Enum('Not Needed', 'ML Recommended', 'Pending Review', 'Staff Referred', 'Completed', 'Rejected', name='referrals_referral_status')
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('patients', schema=None) as batch_op:
-        # === BEGIN DATA MIGRATION FOR patients.status ===
-        op.execute("UPDATE patients SET status = 'NEEDS_ASSESSMENT' WHERE status = 'Needs Assessment'")
-        op.execute("UPDATE patients SET status = 'COMPLETED' WHERE status = 'Completed'")
-        op.execute("UPDATE patients SET status = 'NOT_ASSESSED' WHERE status = 'active'")
-        # === END DATA MIGRATION ===
-
-        batch_op.alter_column('status',
-               existing_type=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=20),
-               type_=patient_status_enum, # Sử dụng Enum đã định nghĩa
-               existing_nullable=True, 
-               nullable=False,
-               postgresql_using='status::patientstatus' 
-              )
-        batch_op.create_index(batch_op.f('ix_patients_status'), ['status'], unique=False)
-
-    with op.batch_alter_table('referrals', schema=None) as batch_op:
-        # === BEGIN DATA MIGRATION FOR referrals.referral_status ===
-        op.execute("UPDATE referrals SET referral_status = 'DIETITIAN_UNASSIGNED' WHERE referral_status = 'ML Recommended'")
-        op.execute("UPDATE referrals SET referral_status = 'DIETITIAN_UNASSIGNED' WHERE referral_status = 'Staff Referred'")
-        op.execute("UPDATE referrals SET referral_status = 'DIETITIAN_UNASSIGNED' WHERE referral_status = 'Pending Review'")
-        op.execute("UPDATE referrals SET referral_status = 'COMPLETED' WHERE referral_status = 'Completed'")
-        op.execute("UPDATE referrals SET referral_status = NULL WHERE referral_status = 'Not Needed'")
-        op.execute("UPDATE referrals SET referral_status = NULL WHERE referral_status = 'Rejected'")
-        # === END DATA MIGRATION ===
-
-        # Thay đổi kiểu cột referral_status
-        batch_op.alter_column('referral_status',
-               existing_type=mysql.ENUM('Not Needed', 'ML Recommended', 'Pending Review', 'Staff Referred', 'Completed', 'Rejected', collation='utf8mb4_unicode_ci'),
-               type_=referral_status_enum, # Sử dụng Enum mới
-               existing_nullable=True, # Giữ nullable cũ (nếu có)
-               nullable=True, # Cho phép NULL theo model mới
-               postgresql_using='referral_status::referralstatus'
-               )
-
-        # Thay đổi liên kết dietitian
-        batch_op.add_column(sa.Column('assigned_dietitian_user_id', sa.Integer(), nullable=True))
-        batch_op.create_foreign_key('fk_referrals_assigned_dietitian_user', 'users', ['assigned_dietitian_user_id'], ['userID'])
-        # Kiểm tra xem khóa ngoại cũ có tồn tại không trước khi xóa
-        # Tên khóa ngoại có thể khác nhau, cần kiểm tra DB hoặc migration trước đó
-        # Giả sử tên là 'referrals_ibfk_3' như trong file gốc
-        try:
-            batch_op.drop_constraint('referrals_ibfk_3', type_='foreignkey')
-        except Exception as e:
-            print(f"Could not drop foreign key constraint 'referrals_ibfk_3': {e}")
-            # Có thể bỏ qua lỗi nếu khóa không tồn tại
-        batch_op.drop_column('dietitianID')
-
-        # Tạo index mới cho referral_status
-        batch_op.create_index(batch_op.f('ix_referrals_referral_status'), ['referral_status'], unique=False)
-
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               type_=sa.Text(length=16777215),
-               existing_nullable=True)
-
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
-        batch_op.alter_column('error_details',
-               existing_type=sa.Text(length=16777215),
-               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
-               existing_nullable=True)
-
-    with op.batch_alter_table('referrals', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_referrals_referral_status'))
-
-        # Thêm lại cột dietitianID và khóa ngoại cũ
-        batch_op.add_column(sa.Column('dietitianID', mysql.INTEGER(), autoincrement=False, nullable=True))
-        batch_op.create_foreign_key('referrals_ibfk_3', 'dietitians', ['dietitianID'], ['dietitianID'])
-        # Xóa cột và khóa ngoại mới
-        batch_op.drop_constraint('fk_referrals_assigned_dietitian_user', type_='foreignkey')
-        batch_op.drop_column('assigned_dietitian_user_id')
-
-        # === BEGIN DATA MIGRATION FOR referrals.referral_status (DOWNGRADE) ===
-        op.execute("UPDATE referrals SET referral_status = 'ML Recommended' WHERE referral_status = 'DIETITIAN_UNASSIGNED' AND is_ml_recommended = TRUE") # Ưu tiên ML nếu is_ml_recommended là True
-        op.execute("UPDATE referrals SET referral_status = 'Staff Referred' WHERE referral_status = 'DIETITIAN_UNASSIGNED' AND is_ml_recommended = FALSE") # Giả định còn lại là Staff Referred
-        op.execute("UPDATE referrals SET referral_status = 'Pending Review' WHERE referral_status = 'WAITING_FOR_REPORT'") # Map tương đối
-        op.execute("UPDATE referrals SET referral_status = 'Completed' WHERE referral_status = 'COMPLETED'")
-        op.execute("UPDATE referrals SET referral_status = 'Not Needed' WHERE referral_status IS NULL") # Map NULL về Not Needed
-        # === END DATA MIGRATION ===
-
-        # Thay đổi kiểu cột referral_status về cũ
-        batch_op.alter_column('referral_status',
-               existing_type=referral_status_enum,
-               type_=old_referral_status_enum_for_downgrade, # Sử dụng Enum cũ
-               existing_nullable=True,
-               nullable=True, # Giữ nullable như cũ?
-               server_default='Not Needed', # Đặt lại default cũ
-               postgresql_using='referral_status::referrals_referral_status'
-               )
-
-    with op.batch_alter_table('patients', schema=None) as batch_op:
-        batch_op.drop_index(batch_op.f('ix_patients_status'))
-
-        # === BEGIN DATA MIGRATION FOR patients.status (DOWNGRADE) ===
-        op.execute("UPDATE patients SET status = 'Needs Assessment' WHERE status = 'NEEDS_ASSESSMENT'")
-        op.execute("UPDATE patients SET status = 'Completed' WHERE status = 'COMPLETED'")
-        op.execute("UPDATE patients SET status = 'active' WHERE status = 'NOT_ASSESSED'")
-        # === END DATA MIGRATION ===
-
-        batch_op.alter_column('status',
-               existing_type=patient_status_enum,
-               type_=mysql.VARCHAR(collation='utf8mb4_unicode_ci', length=20),
-               existing_nullable=False,
-               nullable=True)
-
-    # ### end Alembic commands ###
diff --git a/migrations/versions/fe0cbf650625_add.py b/migrations/versions/fe0cbf650625_add.py
new file mode 100644
index 0000000000000000000000000000000000000000..a517bcc835710accca34ac7e11909f3d237afef9
--- /dev/null
+++ b/migrations/versions/fe0cbf650625_add.py
@@ -0,0 +1,64 @@
+"""add
+
+Revision ID: fe0cbf650625
+Revises: 8116b7d4aede
+Create Date: 2025-04-21 20:14:07.900955
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import mysql
+
+# revision identifiers, used by Alembic.
+revision = 'fe0cbf650625'
+down_revision = '8116b7d4aede'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table('patient_dietitian_assignments',
+    sa.Column('id', mysql.INTEGER(unsigned=True), nullable=False),
+    sa.Column('patient_id', sa.String(length=50), nullable=False),
+    sa.Column('dietitian_id', sa.Integer(), nullable=False),
+    sa.Column('assignment_date', sa.DateTime(), nullable=False),
+    sa.Column('end_date', sa.DateTime(), nullable=True),
+    sa.Column('is_active', sa.Boolean(), nullable=False),
+    sa.Column('notes', sa.Text(), nullable=True),
+    sa.ForeignKeyConstraint(['dietitian_id'], ['users.userID'], ondelete='CASCADE'),
+    sa.ForeignKeyConstraint(['patient_id'], ['patients.patientID'], ondelete='CASCADE'),
+    sa.PrimaryKeyConstraint('id'),
+    sa.UniqueConstraint('patient_id', 'dietitian_id', name='uq_patient_dietitian_assignment')
+    )
+    with op.batch_alter_table('patient_dietitian_assignments', schema=None) as batch_op:
+        batch_op.create_index(batch_op.f('ix_patient_dietitian_assignments_assignment_date'), ['assignment_date'], unique=False)
+        batch_op.create_index(batch_op.f('ix_patient_dietitian_assignments_dietitian_id'), ['dietitian_id'], unique=False)
+        batch_op.create_index(batch_op.f('ix_patient_dietitian_assignments_is_active'), ['is_active'], unique=False)
+        batch_op.create_index(batch_op.f('ix_patient_dietitian_assignments_patient_id'), ['patient_id'], unique=False)
+
+    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
+        batch_op.alter_column('error_details',
+               existing_type=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
+               type_=sa.Text(length=16777215),
+               existing_nullable=True)
+
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    with op.batch_alter_table('uploadedfiles', schema=None) as batch_op:
+        batch_op.alter_column('error_details',
+               existing_type=sa.Text(length=16777215),
+               type_=mysql.LONGTEXT(collation='utf8mb4_unicode_ci'),
+               existing_nullable=True)
+
+    with op.batch_alter_table('patient_dietitian_assignments', schema=None) as batch_op:
+        batch_op.drop_index(batch_op.f('ix_patient_dietitian_assignments_patient_id'))
+        batch_op.drop_index(batch_op.f('ix_patient_dietitian_assignments_is_active'))
+        batch_op.drop_index(batch_op.f('ix_patient_dietitian_assignments_dietitian_id'))
+        batch_op.drop_index(batch_op.f('ix_patient_dietitian_assignments_assignment_date'))
+
+    op.drop_table('patient_dietitian_assignments')
+    # ### end Alembic commands ###