Hệ thống pháp luật

ỦY BAN NHÂN DÂN
THÀNH PHỐ HỒ CHÍ MINH
-------

CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Độc lập - Tự do - Hạnh phúc
---------------

Số: 1452/QĐ-UBND

Thành phố Hồ Chí Minh, ngày 14 tháng 4 năm 2023

 

QUYẾT ĐỊNH

VỀ VIỆC PHÊ DUYỆT QUY TRÌNH NỘI BỘ GIẢI QUYẾT THỦ TỤC HÀNH CHÍNH TRONG LĨNH VỰC ĐƯỜNG BỘ THUỘC PHẠM VI CHỨC NĂNG QUẢN LÝ CỦA SỞ GIAO THÔNG VẬN TẢI

CHỦ TỊCH ỦY BAN NHÂN DÂN THÀNH PHỐ HỒ CHÍ MINH

Căn cứ Luật Tổ chức chính quyền địa phương ngày 19 tháng 6 năm 2015; Luật sửa đổi, bổ sung một số điều của Luật Tổ chức Chính phủ và Luật Tổ chức chính quyền địa phương ngày 22 tháng 11 năm 2019;

Căn cứ Nghị định số 63/2010/NĐ-CP ngày 08 tháng 6 năm 2010 của Chính phủ về kiểm soát thủ tục hành chính; Nghị định số 92/2017/NĐ-CP ngày 07 tháng 8 năm 2017 của Chính phủ sửa đổi, bổ sung một số điều của các nghị định liên quan đến kiểm soát thủ tục hành chính; Nghị định số 61/2018/NĐ-CP ngày 23 tháng 4 năm 2018 của Chính phủ về thực hiện cơ chế một cửa, một cửa liên thông trong giải quyết thủ tục hành chính;

Căn cứ Thông tư số 01/2018/TT-VPCP ngày 23 tháng 11 năm 2018 của Bộ trưởng, Chủ nhiệm Văn phòng Chính phủ hướng dẫn thi hành một số quy định của Nghị định số 61/2018/NĐ-CP ngày 23 tháng 4 năm 2018 của Chính phủ về thực hiện cơ chế một cửa, một cửa liên thông trong giải quyết thủ tục hành chính;

Căn cứ Quyết định số 1802/QĐ-UBND ngày 27 tháng 5 năm 2022 của Chủ tịch Ủy ban nhân dân Thành phố về phê duyệt phương án tái cấu trúc, đơn giản hóa thủ tục hành chính;

Xét đề nghị của Giám đốc Sở Giao thông vận tải tại Tờ trình số 3241/TTr-SGTVT ngày 28 tháng 03 năm 2023,

QUYẾT ĐỊNH:

Điều 1. Phê duyệt kèm theo Quyết định này quy trình nội bộ giải quyết thủ tục hành chính đối với Thủ tục cấp giấy phép thi công xây dựng công trình thiết yếu trong phạm vi bảo vệ kết cấu hạ tầng giao thông đường bộ đã được tái cấu trúc thuộc phạm vi chức năng quản lý của Sở Giao thông vận tải.

Danh mục và nội dung chi tiết của các quy trình nội bộ được đăng tải trên Cổng thông tin điện tử của Văn phòng Ủy ban nhân dân Thành phố tại địa chỉ http://vpub.hochiminhcity.gov.vn/portal/Home/danh-muc-tthc/default.aspx.

Điều 2. Tổ chức thực hiện

1. Các quy trình nội bộ giải quyết thủ tục hành chính đã được phê duyệt là cơ sở để xây dựng quy trình điện tử, thực hiện việc tiếp nhận, giải quyết và trả kết quả thủ tục hành chính trên Hệ thống thông tin giải quyết thủ tục hành chính Thành phố.

2. Cơ quan, đơn vị thực hiện thủ tục hành chính có trách nhiệm:

a) Tuân thủ theo quy trình nội bộ đã được tái cấu trúc khi tiếp nhận, giải quyết thủ tục hành chính cho cá nhân, tổ chức; không tự đặt thêm thủ tục, giấy tờ ngoài quy định pháp luật.

b) Thường xuyên rà soát, cập nhật các quy trình nội bộ giải quyết thủ tục hành chính được phê duyệt mới, sửa đổi, bổ sung, thay thế, hủy bỏ, bãi bỏ khi có biến động theo quy định pháp luật.

Điều 3. Hiệu lực thi hành

Quyết định có hiệu lực thi hành từ ngày ký. Bãi bỏ quy trình nội bộ số 02 tại Danh mục quy trình nội bộ ban hành kèm theo Quyết định số 5111/QĐ-UBND ngày 02 tháng 12 năm 2019 của Chủ tịch Ủy ban nhân dân Thành phố

Điều 4. Chánh Văn phòng Ủy ban nhân dân Thành phố, Giám đốc Sở Giao thông vận tải, Giám đốc Sở Thông tin và Truyền thông và các tổ chức, cá nhân có liên quan chịu trách nhiệm thi hành Quyết định này./.

 


Nơi nhận:
- Như Điều 4;
- Văn phòng Chính phủ (Cục Kiểm soát TTHC);
- TTUB: CT; các PCT;
- VPUB: CPVP;
- Sở Thông tin và Truyền thông (để cập nhật Hệ thống thông tin giải quyết TTHC)
- Trung tâm Tin học, Trung tâm Công báo;
- Lưu: VT, KSTT/H.

CHỦ TỊCH




Phan Văn Mãi

 

QUY TRÌNH ĐÃ ĐƯỢC TÁI CẤU TRÚC

QUY TRÌNH

Cấp phép thi công xây dựng công trình thiết yếu trong phạm vi bảo vệ kết cấu hạ tầng giao thông đường bộ

(Ban hành kèm theo Quyết định số 1452/QĐ-UBND ngày 14 tháng 4 năm 2023 của Chủ tịch Ủy ban nhân dân Thành phố)

I. THÀNH PHẦN HỒ SƠ

STT

Tên hồ sơ

Số lượng

Ghi chú

01

Đơn đề nghị cấp giấy phép thi công công trình (theo mẫu)

01

Bản chính

02

Văn bản chấp thuận xây dựng công trình thiết yếu của cơ quan quản lý đường bộ có thẩm quyền.

01

Bản sao có chứng thực hoặc bản sao kèm theo bản chính để đối chiếu

(Đối với Văn bản chấp thuận của cơ quan quản lý có sử dụng hệ thống văn bản điện tử liên thông thì chỉ cần bản sao)

03

Bản thiết kế bản vẽ thi công, trong đó có biện pháp tổ chức thi công bảo đảm an toàn giao thông đã được phê duyệt theo quy định. Bản vẽ thiết kế bao gồm các thành phần như sau: bình đồ, trắc dọc, trắc ngang, vị trí đoạn tuyến có xây dựng công trình, thể hiện đầy đủ tọa độ, cao độ theo hệ tọa độ chuẩn Quốc gia. Các bản vẽ phải thể hiện đầy đủ vị trí các công trình hiện hữu nằm trong khu vực xây dựng công trình và khu vực ảnh hưởng bởi việc xây dựng công trình, trong đó thành phần hồ sơ đính kèm theo các tập tin định dạng *.dwg và *.pdf hoặc tương tự nhằm phục vụ cập nhật dữ liệu (các tập tin không bắt buộc phải có trong thành phần hồ sơ)

*Đối với các công trình có quy mô nhỏ như: gắn đồng hồ nước riêng lẻ của tổ chức, cá nhân, hồ sơ đấu nối thoát nước vào hệ thống chung của tổ chức, cá nhân: Thành phần hồ sơ thiết kế không bao gồm trắc dọc, trắc ngang, tọa độ, cao độ tuyệt đối.

02

Bản sao có chứng thực hoặc bản sao kèm theo bản chính để đối chiếu

II. NƠI TIẾP NHẬN, TRẢ KẾT QUẢ, THỜI GIAN VÀ LỆ PHÍ

Nơi tiếp nhận và trả kết quả

Thời gian xử lý

Lệ phí

- Trực tiếp: Bộ phận Một cửa - Sở Giao thông vận tải TPHCM (Sở SGTVT), địa chỉ: 63 Lý Tự Trọng, Bến Nghé, Quận 1, Hồ Chí Minh .

- Nộp hồ sơ qua mạng: tại cổng dịch vụ công trực tuyến của thành phố (http://dichvucong.hochiminhcity.gov.vn) và trả kết quả tại Bộ phận Một cửa - Sở SGTVT.

06 ngày làm việc kể từ ngày nhận đủ hồ sơ hợp lệ

Không

III. TRÌNH TỰ XỬ LÝ CÔNG VIỆC

Bước công việc

Nội dung công việc

Trách nhiệm

Thời gian

Hồ sơ/Biểu mẫu

Diễn giải

B1

Nộp hồ sơ

Tổ chức, đơn vị

Giờ hành chính

Theo Mục I

- Chuẩn bị thành phần hồ sơ theo mục I.

- Nộp hồ sơ tại Bộ phận một cửa - Sở GTVT.

Kiểm tra hồ sơ

Bộ phận một cửa

- BM 01

- BM 02

- BM03

- Theo Mục I

- Trường hợp hồ sơ đầy đủ và hợp lệ: Lập Giấy tiếp nhận hồ sơ và hẹn trả kết quả; trao cho người nộp hồ sơ theo BM 01; thực hiện tiếp bước B2.

- Trường hợp hồ sơ chưa đầy đủ hoặc chưa hợp lệ: Hướng dẫn người nộp hồ sơ bổ sung, hoàn thiện hồ sơ và ghi rõ lý do theo BM 02.

- Trường hợp từ chối tiếp nhận hồ sơ: Lập Phiếu từ chối tiếp nhận giải quyết hồ sơ theo BM03.

B2

Tiếp nhận hồ sơ

Bộ phận một cửa

0,25 ngày làm việc (02 giờ)

Theo Mục I

- Tiếp nhận hồ sơ theo quy định.

- Chuyển giao hồ sơ đến phòng chuyên môn.

B9

Phân công xử lý

Lãnh đạo Phòng chuyên môn giải quyết TTHC

0,25 ngày làm việc (02 giờ)

- Theo Mục I

Lãnh đạo Phòng chuyên môn tiếp nhận hồ sơ từ Bộ phận một cửa và phân công chuyên viên thụ lý hồ sơ.

B10

Thẩm định, đề xuất kết quả giải quyết TTHC

Chuyên viên thụ lý giải quyết TTHC

04 ngày làm việc

- Theo Mục I

- Hồ sơ trình:

Tờ trình đề xuất cấp phép

Dự thảo Giấy phép hoặc Văn bản trả lời từ chối cấp phép

- Chuyên viên thụ lý hồ sơ tiến hành xem xét, tổng hợp và đề xuất kết quả giải quyết hồ sơ:

Nếu hồ sơ đạt: soạn Tờ trình, dự thảo Giấy phép.

Nếu hồ sơ không đạt: dự thảo Văn bản trả lời từ chối cấp phép, nêu rõ lý do.

- Trình Lãnh đạo Phòng duyệt ký.

B11

Duyệt ký

Lãnh đạo Phòng chuyên môn giải quyết TTHC

01 ngày làm việc

- Theo Mục I

- Hồ sơ trình

- Giấy phép hoặc Văn bản trả lời từ chối cấp phép

- Lãnh đạo Phòng xem xét hồ sơ ký duyệt Giấy phép hoặc Văn bản trả lời từ chối cấp phép.

- Chuyển hồ sơ cho chuyên viên thụ lý.

B12

Ban hành văn bản

Chuyên viên thụ lý giải quyết TTHC

0,25 ngày làm việc (02 giờ)

Giấy phép hoặc Văn bản trả lời từ chối cấp phép

- Lấy số, pho to Giấy phép hoặc Văn bản trả lời từ chối cấp phép.

- Chuyển Giấy phép hoặc Văn bản trả lời từ chối cấp phép đến Văn thư Sở.

B13

Phát hành Văn bản

Văn thư Sở

0,25 ngày làm việc (02 giờ)

Giấy phép hoặc Văn bản trả lời từ chối cấp phép

- Đóng dấu, phát hành Giấy phép hoặc Văn bản trả lời từ chối cấp phép.

- Chuyển Giấy phép hoặc Văn bản trả lời từ chối cấp phép đến Bộ phận một cửa của Sở.

B15

Trả kết quả

Bộ phận một cửa

Theo Giấy hẹn

Giấy phép hoặc Văn bản trả lời từ chối cấp phép

- Trả kết quả cho tổ chức/đơn vị.

- Thống kê, theo dõi lưu trữ hồ sơ theo quy định.

IV. BIỂU MẪU

Các biểu mẫu sử dụng tại các bước công việc:

STT

Mã hiệu

Tên biểu mẫu

1

BM01

Mẫu Giấy tiếp nhận hồ sơ và hẹn trả kết quả.

2

BM 02

Mẫu Phiếu yêu cầu bổ sung, hoàn thiện hồ sơ

3

BM 03

Mẫu Phiếu từ chối tiếp nhận giải quyết hồ sơ

4

BM 04

Mẫu Đơn đề nghị cấp giấy phép thi công công trình

V. HỒ SƠ CẦN LƯU

STT

Mã hiệu

Tên biểu mẫu

1

BM01

Mẫu Giấy tiếp nhận hồ sơ và hẹn trả kết quả (lưu trên phần mềm)

2

BM 02

Mẫu Phiếu yêu cầu bổ sung và hoàn thiện hồ sơ (nếu có)

3

BM 03

Mẫu Phiếu từ chối giải quyết tiếp nhận hồ sơ (nếu có)

4

BM 04

Đơn đề nghị cấp giấy phép thi công công trình

5

//

Các hồ sơ khác theo văn bản pháp quy hiện hành

VI. CƠ SỞ PHÁP LÝ

- Nghị định số 61/2018/NĐ-CP ngày 23 tháng 4 năm 2018 của Chính phủ về thực hiện cơ chế một cửa, một cửa liên thông trong giải quyết thủ tục hành chính.

- Thông tư 01/2018/TT-VPCP ngày 23 tháng 11 năm 2018 của Văn phòng Chính phủ hướng dẫn thi hành một số quy định của Nghị định số 61/2018/NĐ-CP ngày 23 tháng 4 năm 2018 của Chính phủ về thực hiện cơ chế một cửa, một cửa liên thông trong giải quyết thủ tục hành chính.

- Quyết định số 09/2014/QĐ-UBND ngày 20 tháng 02 năm 2014 của Ủy ban nhân dân thành phố về việc ban hành Quy định về thi công xây dựng công trình thiết yếu trong phạm vi bảo vệ kết cấu hạ tầng giao thông đường bộ trên địa bàn Thành phố Hồ Chí Minh;

- Quyết định 30/2018/QĐ-UBND ngày 04 tháng 9 năm 2018 của Ủy ban nhân dân thành phố Sửa đổi, bổ sung một số điều tại Quyết định số 09/2014/QĐ-UBND ngày 20 tháng 02 năm 2014 của Ủy ban nhân dân thành phố quy định về thi công xây dựng công trình thiết yếu trong phạm vi bảo vệ kết cấu hạ tầng giao thông đường bộ.

 

CÔNG TRÌNH TRONG PHẠM VI BẢO VỆ KẾT CẤU HẠ TẦNG GIAO THÔNG ĐƯỜNG BỘ

(Ban hành kèm theo Quyết định số 09/2014/QĐ-UBND ngày 20 tháng 02 năm 2014 của Ủy ban nhân dân thành phố)

TÊN CƠ QUAN ĐƠN VỊ (1)
CHỦ ĐẦU TƯ (2)
-------

CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Độc lập - Tự do - Hạnh phúc
---------------

Số: ……
V/v trình hồ sơ đề nghị cấp giấy phép thi công công trình ….

TP. Hồ Chí Minh, ngày ... tháng ... năm 202...

 

ĐƠN ĐỀ NGHỊ CẤP PHÉP THI CÔNG CÔNG TRÌNH

Cấp phép thi công (....3….. )

Kính gửi:…………………… (...4...)

- Căn cứ Nghị định số 11/2010/NĐ-CP ngày 24 tháng 02 năm 2010 của Chính phủ Quy định về quản lý và bảo vệ kết cấu hạ tầng giao thông đường bộ;

- Căn cứ Thông tư số 39/2011/TT-BGTVT ngày 18 tháng 5 năm 2011 của Bộ trưởng Bộ Giao thông vận tải hướng dẫn thực hiện một số điều của Nghị định số 11/2010/NĐ-CP ngày 24 tháng 02 năm 2010 của Chính phủ Quy định về quản lý và bảo vệ kết cấu hạ tầng giao thông đường bộ;

- Căn cứ Quy định về thi công xây dựng công trình trong phạm vi bảo vệ kết cấu hạ tầng giao thông đường bộ trên địa bàn Thành phố Hồ Chí Minh ban hành kèm theo Quyết định số     /QĐ-UBND ngày   tháng   năm 2012 của Ủy ban nhân dân Thành phố Hồ Chí Minh;

- Căn cứ (....5....)

- (…. 2….. ) đề nghị được cấp phép thi công (....6....) tại (....7....). Thời gian thi công bắt đầu từ ngày.... tháng....năm .... đến hết ngày... tháng...năm...

Xin gửi kèm theo các tài liệu sau:

(... 5...) (bản sao có xác nhận của Chủ đầu tư).

(...8...) (bản chính).

(...9...)

(....2....) xin cam kết tự di chuyển hoặc cải tạo công trình và không đòi bồi thường khi ngành đường bộ có yêu cầu di chuyển hoặc cải tạo; đồng thời, hoàn chỉnh các thủ tục theo quy định của pháp luật có liên quan để công trình được triển khai xây dựng trong thời hạn có hiệu lực của Văn bản chấp thuận. Đồng thời (....2....) xin cam kết thực hiện đầy đủ các biện pháp bảo đảm giao thông thông suốt, an toàn theo quy định, hạn chế ùn tắc giao thông đến mức cao nhất và không gây ô nhiễm môi trường.

(...2...) xin cam kết thi công theo đúng Hồ sơ thiết kế đã được (...10...) phê duyệt và tuân thủ theo quy định của Giấy phép thi công. Nếu thi công không thực hiện các biện pháp bảo đảm giao thông thông suốt, an toàn theo quy định, để xảy ra tai nạn giao thông, ùn tắc giao thông, ô nhiễm môi trường nghiêm trọng, (...2...) chịu trách nhiệm theo quy định của pháp luật.

Địa chỉ liên hệ: ……………………….

Số điện thoại: ………………………..

 


Nơi nhận:
- Như trên;
- ………………;
- ………………;
- Lưu VT.

(……. 2......)
QUYỀN HẠN, CHỨC VỤ CỦA NGƯỜI KÝ
(ký, ghi rõ họ tên và đóng dấu)

 

Hướng dẫn ghi trong Đơn đề nghị

(1) Tên tổ chức hoặc cơ quan cấp trên của đơn vị hoặc tổ chức đứng Đơn đề nghị (nếu có)

(2) Tên đơn vị hoặc tổ chức đứng Đơn đề nghị cấp phép thi công (công trình hoặc thi công trên đường bộ đang khai thác).

(3) Ghi vắn tắt tên công trình hoặc hạng mục công trình đề nghị cấp phép, quốc lộ, địa phương; ví dụ “Cấp phép thi công đường ống cấp nước sinh hoạt trong phạm vi bảo vệ kết cấu hạ tầng giao thông đường bộ của Quốc lộ 1, quận 12”.

(4) Tên cơ quan cấp phép thi công (Sở Giao thông vận tải hoặc Ủy ban nhân dân các quận, huyện);

(5) Văn bản chấp thuận xây dựng hoặc chấp thuận thiết kế công trình của cơ quan quản lý đường bộ có thẩm quyền.

(6) Ghi đầy đủ tên công trình hoặc hạng mục công trình đề nghị cấp phép thi công.

(7) Ghi đầy đủ rõ lý trình, tên đường quốc lộ, thuộc địa bàn quận huyện nào.

(8) Hồ sơ Thiết kế bản vẽ thi công (trong đó có Biện pháp tổ chức thi công đảm bảo an toàn giao thông) đã được cấp có thẩm quyền phê duyệt.

(9) Các tài liệu khác nếu (....2....) thấy cần thiết.

(10) Cơ quan phê duyệt Thiết kế bản vẽ thi công./.

 



lồng nhau (bên trong) hay không const memberID = 0; const vbID = 'e9e89479e9a6d7ac5829afb4cc3aef63'; // State management cho phân tích let isAnalyzing = false; // Có đang phân tích không let currentAnalyzingAddress = null; // Address đang được phân tích let currentAnalyzingElement = null; // Element đang được phân tích let currentAnalyzingBadge = null; // Badge của element đang phân tích let isPanelOpen = false; // Panel phân tích có đang mở không console.log('Tiện ích loaded - memberID:', memberID, 'vbID:', vbID); function isInViewportAndTabNoiDung(element) { const rect = element.getBoundingClientRect(); const buffer = 1500; // Buffer to preload content below the viewport (approx. 50+ lines) const viewHeight = window.innerHeight || document.documentElement.clientHeight; const isInViewport = rect.top < viewHeight + buffer && rect.bottom >= 0; const isInTabNoiDung = $(element).closest('#tab_noi_dung_vb').length > 0; return isInViewport && isInTabNoiDung; } function getAddress(element) { const validTags = ['trichyeu', 'cancu', 'phan', 'chuong', 'muc', 'tieumuc', 'dieu', 'khoan', 'diem']; const $parent = $(element).closest(validTags.join(',')); if (!$parent.length) { return null; } let addr = $parent.attr('address'); if (!addr && $parent.prop('tagName').toLowerCase() === 'trichyeu') { addr = 'trichyeu'; $parent.attr('address', addr); } return addr || null; } function processTnplClasses($element) { const tnplKeysInLine = new Set(); // key = slug hoặc text (thường là slug) $element.find('tnpl').each(function () { const $tnpl = $(this); const tnplSlug = ($tnpl.attr('slug') || '').trim().toLowerCase(); const tnplKey = tnplSlug || $tnpl.text().trim().toLowerCase(); // Đã xử lý trong cùng dòng => bỏ if (tnplKeysInLine.has(tnplKey)) { return; } tnplKeysInLine.add(tnplKey); let tnplExists = false; // Chỉ duyệt các tnpl đã được tô màu (class on) $('tnpl.on').each(function () { const $existingTnpl = $(this); const existingSlug = ($existingTnpl.attr('slug') || '').trim().toLowerCase(); const existingKey = existingSlug || $existingTnpl.text().trim().toLowerCase(); if ( existingKey === tnplKey && isInViewportAndTabNoiDung($existingTnpl[0]) ) { tnplExists = true; return false; // break each } }); if (!tnplExists) { $tnpl.addClass('on'); } }); } function processQueue() { while (pendingRequests < maxConcurrentRequests && requestQueue.length > 0) { const task = requestQueue.shift(); pendingRequests++; task() .always(() => { pendingRequests--; processQueue(); }); } } function processVisibleParagraphs() { try { $('#tab_noi_dung_vb p:not([is-posted="1"])').each(function () { let $element = $(this); if (isInViewportAndTabNoiDung(this)) { $element.attr('is-posted', '1'); $element.addClass('loading-content'); let p_innerHTML = $element.html(); let address = null; if (cac_cau_hinh.loai_noi_dung.includes('docs')) { address = getAddress($element); } const isSubP = $element.parents('p').length > 0; if (isSubP && !allow_sub_p) { $element.removeClass('loading-content'); return; // Không gửi nếu không cho phép } const postData = { p_content: p_innerHTML, cac_cau_hinh, address }; if (isSubP && allow_sub_p) { postData.sub_p = 1; } requestQueue.push(() => $.ajax({ url: '//tnpl' + (Math.floor(Math.random() * 10) + 1) + '.hethongphapluat.com/tien-ich/tim.tien.ich.php', type: 'POST', data: postData, success: function(response) { $element.html(response); processTnplClasses($element); // Đợi CTTD và các tiện ích load xong rồi mới attach badge if (memberID === 4 && typeof attachPhanTichBadge === 'function') { setTimeout(function() { // $element chính là thẻ p, kiểm tra và attach badge trực tiếp const $parent = $element.closest('phan, chuong, muc, tieumuc, dieu, khoan, diem'); if ($parent.length > 0 && $parent.find('.badge-phan-tich[data-for="' + $parent.attr('address') + '"]').length === 0) { const address = $parent.attr('address'); $element.attr('data-address', address); // Lấy tên loại thẻ cho tooltip const parentType = getParentTypeName($parent.prop('tagName').toLowerCase()); // Append badge VÀO PARENT (dieu, khoan,...) thay vì vào

để tránh xung đột CTTD const $badge = $('Phân tích'); $parent.append($badge); // Thêm class để CSS set position: relative CHỈ cho element có badge $parent.addClass('has-phan-tich-badge'); } // Xử lý các p con (nếu có sub-p) attachPhanTichBadge($element); }, 300); // Đợi 300ms để CTTD render xong } }, complete: function() { $element.removeClass('loading-content'); } }) ); processQueue(); } }); } catch(e) { console.error('Lỗi processVisibleParagraphs:', e); } } $(window).on('scroll resize', function () { processVisibleParagraphs(); }); console.log('Bắt đầu processVisibleParagraphs lần đầu...'); processVisibleParagraphs(); console.log('processVisibleParagraphs lần đầu hoàn thành'); // Chức năng phân tích điều luật (chỉ cho member_id = 4) if (memberID === 4) { // Modal cảnh báo function showWarningModal(message) { // Tạo modal nếu chưa có if ($('#warningModal').length === 0) { const modalHTML = `

`; $('body').append(modalHTML); } $('#warningModalBody').html('

' + message + '

'); $('#warningModal').modal('show'); } // Hàm lấy tên tiếng Việt của thẻ function getParentTypeName(tagName) { const typeNames = { 'phan': 'Phần', 'chuong': 'Chương', 'muc': 'Mục', 'tieumuc': 'Tiểu mục', 'dieu': 'Điều', 'khoan': 'Khoản', 'diem': 'Điểm' }; return typeNames[tagName] || 'Nội dung'; } function attachPhanTichBadge($container) { const validTags = 'phan, chuong, muc, tieumuc, dieu, khoan, diem'; $container.find('p').each(function() { const $p = $(this); const $parent = $p.closest(validTags); if ($parent.length > 0) { const address = $parent.attr('address'); // Kiểm tra đã có badge cho parent này chưa if ($parent.find('.badge-phan-tich[data-for="' + address + '"]').length === 0) { // Lưu address vào data attribute $p.attr('data-address', address); // Lấy tên loại thẻ cho tooltip const parentType = getParentTypeName($parent.prop('tagName').toLowerCase()); // Append badge vào PARENT, không vào

const $badge = $('Phân tích'); $parent.append($badge); // Thêm class để CSS set position: relative CHỈ cho element có badge $parent.addClass('has-phan-tich-badge'); } } }); } // Helper: Escape HTML entities function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return String(text).replace(/[&<>"']/g, function(m) { return map[m]; }); } // Helper: Convert Markdown to HTML (đơn giản) function markdownToHtml(markdown) { if (!markdown) return ''; let html = markdown; // Headers html = html.replace(/^### (.*$)/gim, '

$1
'); html = html.replace(/^## (.*$)/gim, '

$1

'); html = html.replace(/^# (.*$)/gim, '

$1

'); // Bold html = html.replace(/\*\*(.*?)\*\*/g, '$1'); // Italic html = html.replace(/\*(.*?)\*/g, '$1'); // Blockquote html = html.replace(/^> (.*$)/gim, '
$1
'); html = html.replace(/^> (.*$)/gim, '
$1
'); // Lists (unordered) html = html.replace(/^\- (.*$)/gim, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>)/s, '
      $1
    '); // Lists (ordered) html = html.replace(/^\d+\. (.*$)/gim, '
  • $1
  • '); // Line breaks và paragraphs html = html.split('\n\n').map(para => { para = para.trim(); if (para.startsWith('')) { return para; } if (para) { return '

    ' + para.replace(/\n/g, '
    ') + '

    '; } return ''; }).join('\n'); // Clean up multiple line breaks html = html.replace(/\n{3,}/g, '\n\n'); return html; } // Panel fixed position function closePhanTichPanel() { const $panel = $('#phanTichPanel'); if ($panel.length) { $panel.removeClass('show'); setTimeout(() => { $panel.remove(); }, 300); } // Reset highlight và badge khi đóng panel if (currentAnalyzingElement) { currentAnalyzingElement.removeClass('highlight-border-persistent'); } if (currentAnalyzingBadge) { currentAnalyzingBadge.text('Phân tích').removeClass('analyzing'); currentAnalyzingBadge.data('analyzing', false); currentAnalyzingBadge.data('hovering', false); currentAnalyzingBadge.css({display: 'none'}); // Ẩn badge khi đóng } // Reset tất cả các element khác (trong trường hợp có nhiều) $('#tab_noi_dung_vb .highlight-border-persistent').removeClass('highlight-border-persistent'); $('#tab_noi_dung_vb .badge-phan-tich-container.analyzing').each(function() { $(this).text('Phân tích').removeClass('analyzing').data('analyzing', false); }); // Check: có CTTD pointer đang mở không? const $visiblePointers = $('.pointer:visible'); const hadCTTDOpen = $visiblePointers.length > 0; if (hadCTTDOpen) { // CÓ CTTD đang mở → giữ rightdocinfo ẩn console.log('ℹ️ CTTD pointer is visible, keeping rightdocinfo hidden'); } else { // KHÔNG có CTTD → SHOW lại rightdocinfo const $rightdocinfo = $('#rightdocinfo'); if ($rightdocinfo.length > 0) { $rightdocinfo.show(); console.log('✅ Showing rightdocinfo back (no CTTD pointer)'); } } // Reset state isAnalyzing = false; currentAnalyzingAddress = null; currentAnalyzingElement = null; currentAnalyzingBadge = null; isPanelOpen = false; // Đánh dấu panel đã đóng console.log('✅ Panel closed, state reset, isPanelOpen = false'); } // Panel đã song song với rightdocinfo → không cần MutationObserver nữa console.log('✅ Panel running in standalone mode (parallel to rightdocinfo)'); // Resize event để update panel dimensions khi browser resize let resizeTimer; $(window).on('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { if (isPanelOpen && $('#phanTichPanel').length > 0) { updatePanelDimensions(); console.log('✅ Panel dimensions updated on window resize'); } }, 250); // Debounce 250ms }); // Function để detect và áp dụng dimensions từ rightdocinfo function updatePanelDimensions() { const $panel = $('#phanTichPanel'); const $rightdocinfo = $('#rightdocinfo'); const $docRightCol = $('#doc-right-col'); if ($panel.length === 0) return; // Ưu tiên: doc-right-col > rightdocinfo let $reference = $docRightCol.length > 0 ? $docRightCol : $rightdocinfo; // Nếu reference bị ẩn (display:none), tạm show để get dimensions let wasHidden = false; if ($reference.length > 0 && !$reference.is(':visible')) { wasHidden = true; $reference.css('visibility', 'hidden').show(); } if ($reference.length > 0) { const refWidth = $reference.outerWidth(); const refOffset = $reference.offset(); if (refWidth && refOffset) { // Tính vị trí right từ edge màn hình const windowWidth = $(window).width(); const rightPosition = windowWidth - (refOffset.left + refWidth); $panel.css({ 'width': refWidth + 'px', 'right': rightPosition + 'px' }); console.log('✅ Panel dimensions updated:', { width: refWidth + 'px', right: rightPosition + 'px', reference: $reference.attr('id') }); } else { console.warn('⚠️ Could not get dimensions from reference element'); } // Restore trạng thái hidden nếu cần if (wasHidden) { $reference.hide().css('visibility', ''); } } else { console.warn('⚠️ No reference element found for panel dimensions'); } } function openPhanTichPanel(address, vbID) { console.log('openPhanTichPanel called with address:', address); console.log('Current state - isAnalyzing:', isAnalyzing, 'currentAnalyzingAddress:', currentAnalyzingAddress); // Kiểm tra nếu đang phân tích element khác if (isAnalyzing && currentAnalyzingAddress && currentAnalyzingAddress !== address) { const currentName = getElementDisplayName(currentAnalyzingAddress); console.warn('Already analyzing:', currentAnalyzingAddress, 'Cannot analyze:', address); showWarningModal('Vui lòng chờ phân tích ' + currentName + ' hoàn tất...'); return; } // Nếu đang phân tích cùng element → không làm gì if (isAnalyzing && currentAnalyzingAddress === address) { console.log('Already analyzing this element, ignoring duplicate request'); return; } // Panel sẽ fixed position append vào body const $rightdocinfo = $('#rightdocinfo'); // KHÔNG ẨN CTTD pointer - cho phép CTTD và panel cùng tồn tại console.log('Panel opening, CTTD pointer can stay visible'); // ẨN rightdocinfo để tiết kiệm không gian if ($rightdocinfo.length > 0) { $rightdocinfo.hide(); console.log('Hidden rightdocinfo to save space'); } // XÓA highlight persistent của TẤT CẢ elements cũ trước $('#tab_noi_dung_vb .highlight-border-persistent').removeClass('highlight-border-persistent'); console.log('Removed all previous highlight-border-persistent'); // Tìm element đang được phân tích và badge của nó const $element = $('[address="' + address + '"]'); const $badge = $element.find('.badge-phan-tich-container[data-for="' + address + '"]').first(); // Set state isAnalyzing = true; currentAnalyzingAddress = address; currentAnalyzingElement = $element; currentAnalyzingBadge = $badge; console.log('State set:', { isAnalyzing: isAnalyzing, currentAnalyzingAddress: currentAnalyzingAddress, elementFound: $element.length > 0, badgeFound: $badge.length > 0 }); // Thêm highlight persistent cho element MỚI này $element.addClass('highlight-border-persistent'); // Thay đổi badge thành "Đang phân tích..." và giữ hiển thị if ($badge.length > 0) { $badge.text('Đang phân tích...').addClass('analyzing'); // Giữ badge hiển thị và ở đúng vị trí $badge.data('analyzing', true); $badge.data('hovering', true); // Prevent auto-hide console.log('Badge set to analyzing state'); // Đảm bảo badge hiển thị ở đúng vị trí (vì dùng position: fixed) showPhanTichBadgeForParent($element); } // Tạo panel nếu chưa có - fixed position append vào body if ($('#phanTichPanel').length === 0) { const panelHTML = `
    Phân tích điều luật
    Đang phân tích...

    Đang phân tích...

    `; // Append vào body (fixed position không cần container cụ thể) $('body').append(panelHTML); // Detect width từ rightdocinfo và áp dụng cho panel updatePanelDimensions(); // Trigger show và set flag setTimeout(() => { $('#phanTichPanel').addClass('show'); isPanelOpen = true; console.log('✅ Panel opened (fixed position), isPanelOpen = true'); }, 10); } else { $('#phanTichPanelBody').html(`
    Đang phân tích...

    Đang phân tích...

    `); // Update dimensions khi re-open updatePanelDimensions(); $('#phanTichPanel').addClass('show'); isPanelOpen = true; console.log('✅ Panel re-opened (fixed position), isPanelOpen = true'); } // Bind nút đóng và ESC $(document).off('click.closePhanTich').on('click.closePhanTich', '.close-phan-tich', function() { closePhanTichPanel(); }); $(document).off('keyup.closePhanTich').on('keyup.closePhanTich', function(e) { if (e.key === 'Escape') closePhanTichPanel(); }); // Bind nút refresh - phân tích lại $(document).off('click.refreshPhanTich').on('click.refreshPhanTich', '.btn-refresh-phan-tich', function(e) { e.preventDefault(); e.stopPropagation(); const $btn = $(this); const $icon = $btn.find('i'); // Disable button và thêm animation $btn.prop('disabled', true); $icon.addClass('fa-spin'); console.log('🔄 Refresh: Phân tích lại address:', address); // Show loading trong panel $('#phanTichPanelBody').html(`
    Đang phân tích lại...

    Đang xóa cache và phân tích lại...

    `); // Gọi API xóa cache trước deletePhanTichCache(address, vbID, function(deleteSuccess) { if (deleteSuccess) { console.log('✅ Cache deleted, now re-analyzing...'); // Sau khi xóa cache, gọi lại API phân tích callPhanTichAPI(address, vbID, function() { // Enable lại button $btn.prop('disabled', false); $icon.removeClass('fa-spin'); }); } else { console.error('❌ Failed to delete cache'); $('#phanTichPanelBody').html(` `); $btn.prop('disabled', false); $icon.removeClass('fa-spin'); } }); }); // Gọi API phân tích (dùng function helper) callPhanTichAPI(address, vbID); } // Helper: Gọi API phân tích (tách riêng để dùng lại) function callPhanTichAPI(address, vbID, callback) { const randomServer = Math.floor(Math.random() * 10) + 1; $.ajax({ url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/phan.tich.dieu.luat.php', type: 'POST', contentType: 'application/json', timeout: 300000, // 5 phút data: JSON.stringify({ address: address, vb_id: vbID }), success: function(response) { console.log('Analysis complete for:', address, response); // Reset badge về trạng thái bình thường (nhưng vẫn hiển thị) if (currentAnalyzingBadge) { currentAnalyzingBadge.text('Phân tích').removeClass('analyzing'); currentAnalyzingBadge.data('analyzing', false); console.log('Badge reset to normal state'); } // Reset state analyzing để có thể phân tích element khác isAnalyzing = false; console.log('State reset: isAnalyzing = false, can analyze other elements now'); if (response.ok) { // Render kết quả phân tích let html = ''; html += '
    '; html += '
    ' + escapeHtml(response.ten_van_ban) + '
    '; if (response.so_hieu) { html += 'Số hiệu: ' + escapeHtml(response.so_hieu) + '
    '; } html += 'Điều khoản: ' + escapeHtml(response.address) + ''; if (response.from_cache) { html += ' Cache'; } html += '
    '; html += '
    ' + markdownToHtml(response.phan_tich) + '
    '; if (response.usage) { html += '
    '; html += 'Thống kê: '; html += 'Input tokens: ' + (response.usage.promptTokenCount || 0) + ', '; html += 'Output tokens: ' + (response.usage.candidatesTokenCount || 0); html += '
    '; } $('#phanTichPanelBody').html(html); } else { $('#phanTichPanelBody').html(` `); } if (callback) callback(); }, error: function(xhr, status, error) { console.error('Analysis error:', error); // Reset badge về trạng thái bình thường if (currentAnalyzingBadge) { currentAnalyzingBadge.text('Phân tích').removeClass('analyzing'); currentAnalyzingBadge.data('analyzing', false); } // Reset state analyzing isAnalyzing = false; let errorMsg = error; if (xhr.responseJSON && xhr.responseJSON.error) { errorMsg = xhr.responseJSON.error; } $('#phanTichPanelBody').html(` `); if (callback) callback(); } }); } // Helper: Xóa cache phân tích function deletePhanTichCache(address, vbID, callback) { const randomServer = Math.floor(Math.random() * 10) + 1; $.ajax({ url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/delete.phan.tich.cache.php', type: 'POST', contentType: 'application/json', timeout: 10000, data: JSON.stringify({ address: address, vb_id: vbID }), success: function(response) { console.log('Delete cache response:', response); if (callback) callback(response.ok || false); }, error: function(xhr, status, error) { console.error('Delete cache error:', error); if (callback) callback(false); } }); } // Helper: Lấy tên hiển thị của element từ address function getElementDisplayName(address) { if (!address) return 'nội dung'; const $element = $('[address="' + address + '"]'); if ($element.length === 0) return address; // Parse address: vd "dieu_3_khoan_29" -> "Khoản 29 Điều 3" // Address format: lớn đến nhỏ (phan > chuong > muc > dieu > khoan > diem) const parts = address.split('_'); const displayParts = []; for (let i = 0; i < parts.length; i += 2) { if (i + 1 < parts.length) { const type = getParentTypeName(parts[i]); const num = parts[i + 1]; displayParts.push(type + ' ' + num); } } // Reverse để hiển thị từ nhỏ đến lớn: "Khoản 29 Điều 3" (thay vì "Điều 3 Khoản 29") return displayParts.reverse().join(' '); } function openPhanTichModal(address, vbID) { // Tạo modal nếu chưa có if ($('#modalPhanTich').length === 0) { const modalHTML = ` `; $('body').append(modalHTML); } // Reset và hiển thị modal với loading $('#modalPhanTichBody').html(`
    Đang phân tích...

    Đang phân tích...

    `); $('#modalPhanTich').modal('show'); // AJAX request const randomServer = Math.floor(Math.random() * 10) + 1; $.ajax({ url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/phan.tich.dieu.luat.php', type: 'POST', contentType: 'application/json', data: JSON.stringify({ address: address, vb_id: vbID }), success: function(response) { if (response.ok) { // Render kết quả phân tích let html = ''; // Header thông tin văn bản html += '
    '; html += '
    ' + escapeHtml(response.ten_van_ban) + '
    '; if (response.so_hieu) { html += 'Số hiệu: ' + escapeHtml(response.so_hieu) + '
    '; } html += 'Điều khoản: ' + escapeHtml(response.address) + ''; html += '
    '; // Nội dung phân tích (Markdown -> HTML) html += '
    '; html += markdownToHtml(response.phan_tich); html += '
    '; // Thông tin usage (nếu có) if (response.usage) { html += '
    '; html += 'Thống kê: '; html += 'Input tokens: ' + (response.usage.promptTokenCount || 0) + ', '; html += 'Output tokens: ' + (response.usage.candidatesTokenCount || 0); html += '
    '; } $('#modalPhanTichBody').html(html); } else { $('#modalPhanTichBody').html(` `); } }, error: function(xhr, status, error) { let errorMsg = error; if (xhr.responseJSON && xhr.responseJSON.error) { errorMsg = xhr.responseJSON.error; } $('#modalPhanTichBody').html(` `); } }); } // Helpers: show/hide badge cho parent element (dieu, khoan,...) với position: fixed function showPhanTichBadgeForParent($parent) { // Lấy badge CỦA CHÍNH parent này (match data-for với address của parent) const parentAddress = $parent.attr('address'); const $badge = $parent.find('.badge-phan-tich-container[data-for="' + parentAddress + '"]').first(); if ($badge.length === 0) { console.warn('No badge found for parent:', parentAddress); return; } // Ẩn TẤT CẢ các badge khác để tránh overlap $('.badge-phan-tich-container').not($badge).each(function() { const $otherBadge = $(this); // Chỉ ẩn badge KHÔNG đang analyzing if (!$otherBadge.data('analyzing')) { $otherBadge.css({display: 'none'}); } }); // Show badge tạm để tính width $badge.css({display: 'inline-block', opacity: 0, visibility: 'hidden'}); const badgeWidth = $badge.outerWidth(); // Tính toán vị trí fixed dựa trên offset của parent const offset = $parent.offset(); const scrollTop = $(window).scrollTop(); const scrollLeft = $(window).scrollLeft(); // Position badge top-right của parent và show $badge.css({ display: 'inline-block', visibility: 'visible', opacity: 1, top: (offset.top - scrollTop) + 'px', left: (offset.left + $parent.outerWidth() - badgeWidth - scrollLeft - 5) + 'px' // -5px padding }); console.log('Showing badge for:', parentAddress, 'at position:', $badge.css('top'), $badge.css('left')); $parent.addClass('highlight-border'); } function hidePhanTichBadgeForParent($parent) { const $badge = $parent.find('.badge-phan-tich-container').first(); if ($badge.length === 0) return; $badge.css({display: 'none', opacity: 0}); $parent.removeClass('highlight-border'); } // Biến lưu element đang hover let currentHoveredElement = null; let hoverDebounceTimer = null; // Dùng mousemove để track chính xác element nào đang được hover $(document).on('mousemove', '#tab_noi_dung_vb', function(e) { // Tìm element gần nhất (phan, chuong, muc, dieu, khoan, diem) tại vị trí chuột const $target = $(e.target).closest('phan, chuong, muc, tieumuc, dieu, khoan, diem'); if ($target.length === 0) { // Không hover vào element nào return; } const address = $target.attr('address'); // Nếu đang hover vào cùng element → skip if (currentHoveredElement && currentHoveredElement[0] === $target[0]) { return; } // Clear debounce timer cũ if (hoverDebounceTimer) { clearTimeout(hoverDebounceTimer); } // Debounce để tránh trigger quá nhiều hoverDebounceTimer = setTimeout(function() { // Element thay đổi console.log('Hover changed to:', address); // Set flag hovering cho element mới $target.data('hovering', true); // Cancel timeout nếu có const timeoutId = $target.data('hideTimeout'); if (timeoutId) { clearTimeout(timeoutId); } // Ẩn badge của TẤT CẢ elements khác $('#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem') .not($target) .each(function() { const $el = $(this); // Chỉ xóa highlight-border, KHÔNG xóa highlight-border-persistent $el.removeClass('highlight-border'); // Ẩn badge nếu KHÔNG đang analyzing const $badge = $el.find('.badge-phan-tich-container'); if ($badge.length && !$badge.data('analyzing')) { $badge.css({display: 'none'}); } }); // Attach badge nếu chưa có if (address && $target.find('.badge-phan-tich-container[data-for="' + address + '"]').length === 0) { const parentType = getParentTypeName($target.prop('tagName').toLowerCase()); const $badge = $('Phân tích'); console.log('Creating badge for address:', address, 'parentType:', parentType); $target.append($badge); $target.addClass('has-phan-tich-badge'); } // Show badge cho element này if ($target.find('.badge-phan-tich-container').length > 0) { showPhanTichBadgeForParent($target); } // Update current hovered element currentHoveredElement = $target; }, 50); // Debounce 50ms }); // Event delegation cho hover ra khỏi #tab_noi_dung_vb $(document).on('mouseleave', '#tab_noi_dung_vb', function(e) { // Clear current hovered element currentHoveredElement = null; // Ẩn tất cả badge không đang analyzing sau một khoảng thời gian setTimeout(function() { if (currentHoveredElement === null) { // Chỉ ẩn nếu thực sự không hover vào element nào $('#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem') .each(function() { const $el = $(this); const $badge = $el.find('.badge-phan-tich-container'); if ($badge.length && !$badge.data('analyzing')) { $badge.css({display: 'none'}); } }); console.log('Mouse left tab_noi_dung_vb, hiding all badges'); } }, 300); }); // Event delegation cho hover ra khỏi parent (giữ lại cho badge behavior) $(document).on('mouseleave', '#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem', function(e) { const $parent = $(this); const parentAddress = $parent.attr('address'); const $badge = $parent.find('.badge-phan-tich-container[data-for="' + parentAddress + '"]').first(); // Set flag parent not hovering $parent.data('hovering', false); // Nếu badge đang analyzing thì KHÔNG ẩn, GIỮ hiển thị if ($badge.length > 0 && $badge.data('analyzing')) { console.log('Badge is analyzing, keep visible'); return; } // Delay để có thời gian di chuột vào badge const timeoutId = setTimeout(() => { // Chỉ ẩn nếu cả parent và badge đều không hover và không analyzing if ($badge.length > 0 && !$parent.data('hovering') && !$badge.data('hovering') && !$badge.data('analyzing')) { hidePhanTichBadgeForParent($parent); } }, 300); // Tăng lên 300ms $parent.data('hideTimeout', timeoutId); }); // Hover vào badge → giữ hiển thị $(document).on('mouseenter', '.badge-phan-tich-container', function(e) { e.stopPropagation(); const $badge = $(this); const $parent = $badge.parent(); $badge.data('hovering', true); // Cancel timeout của parent const timeoutId = $parent.data('hideTimeout'); if (timeoutId) { clearTimeout(timeoutId); } }); // Hover ra khỏi badge → ẩn nếu không hover parent $(document).on('mouseleave', '.badge-phan-tich-container', function(e) { const $badge = $(this); $badge.data('hovering', false); const $parent = $badge.parent(); // Nếu badge đang analyzing thì KHÔNG ẩn, GIỮ hiển thị if ($badge.data('analyzing') || $badge.hasClass('analyzing')) { console.log('Badge is analyzing on mouseleave, keep visible'); return; } setTimeout(() => { // Chỉ ẩn nếu cả parent và badge đều không hover và không analyzing if (!$parent.data('hovering') && !$badge.data('hovering') && !$badge.data('analyzing') && !$badge.hasClass('analyzing')) { hidePhanTichBadgeForParent($parent); } }, 300); }); // Event delegation cho hover vào badge → hiện tooltip $(document).on('mouseenter', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function() { const $badge = $(this); const parentType = $badge.attr('data-parent-type') || 'Nội dung'; if ($badge.find('.badge-tooltip').length === 0) { const $tooltip = $('Phân tích chi tiết nội dung ' + parentType + ' này'); $badge.append($tooltip); setTimeout(() => $tooltip.addClass('show'), 10); } }); // Event delegation cho hover ra khỏi badge → ẩn tooltip $(document).on('mouseleave', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function() { const $tooltip = $(this).find('.badge-tooltip'); if ($tooltip.length > 0) { $tooltip.removeClass('show'); setTimeout(() => $tooltip.remove(), 300); } }); // Event delegation cho click badge → mở panel $(document).on('click', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function(e) { e.preventDefault(); e.stopPropagation(); const $badge = $(this); console.log('Badge clicked! Element:', $badge[0]); console.log('Badge parent:', $badge.parent().prop('tagName'), $badge.parent().attr('address')); console.log('Badge data-for:', $badge.attr('data-for')); console.log('Badge data-parent-type:', $badge.attr('data-parent-type')); // Nếu badge đang analyzing thì không cho click if ($badge.hasClass('analyzing') || $badge.data('analyzing')) { console.log('Badge is analyzing, click ignored'); return; } // Lấy address từ data-for attribute const address = $badge.attr('data-for'); console.log('Will analyze address:', address, 'vbID:', vbID); if (address && vbID) { openPhanTichPanel(address, vbID); } else { console.error('Missing address or vbID', {address, vbID}); showWarningModal('Không tìm thấy địa chỉ điều luật hoặc ID văn bản!'); } }); // Ẩn badge khi click vào CTTD $(document).on('click', 'cttd.chuthichtudong span, dctk span, dctd span', function(e) { console.log('CTTD clicked'); // Ẩn TẤT CẢ badge KHÔNG đang analyzing $('.badge-phan-tich-container').each(function() { const $badge = $(this); if (!$badge.data('analyzing') && !$badge.hasClass('analyzing')) { $badge.css({display: 'none'}); console.log('Hiding badge:', $badge.attr('data-for')); } }); }); // Update badge position khi scroll hoặc resize (vì dùng position: fixed) function updateBadgePositions() { $('.badge-phan-tich-container:visible').each(function() { const $badge = $(this); const $parent = $badge.parent(); // Cập nhật position nếu parent đang hover HOẶC badge đang analyzing if ($parent.length && ($parent.is(':hover') || $badge.data('analyzing'))) { // Re-calculate position const offset = $parent.offset(); const scrollTop = $(window).scrollTop(); const scrollLeft = $(window).scrollLeft(); const badgeWidth = $badge.outerWidth(); $badge.css({ top: (offset.top - scrollTop) + 'px', left: (offset.left + $parent.outerWidth() - badgeWidth - scrollLeft - 5) + 'px' }); } }); } $(window).on('scroll', updateBadgePositions); $(window).on('resize', updateBadgePositions); } });