Dọn dẹp Trung tâm điều hành — punch-list

Tổng hợp các việc cần xử lý từ phiên rà soát ngày 11/06/2026 · DRAFT — INTERNAL · không phân phối

Cập nhật 12/06/2026: A1–A5, C1–C4, B1 xong từ phiên trước. D8 · D6 (sub-plan) · D2 · D3 · D1 · D7 · D5 · D4 đã làm và đã deploy lên hub sống — toàn bộ stack app/services + care dashboard/jobs (rev management-hub mới), và D4 đã chạy trên dữ liệu sống. Còn lại: 2 quyết định ở D6 + kiểm tra giao diện khi đăng nhập.

Mỗi mục dưới đây là một việc cụ thể, kèm chẩn đoán (file/dòng), cách sửa đề xuất, và trạng thái. Các mục ĐÃ SỬA đã được sửa trong code ở phiên này. Các mục CẦN QUYẾT ĐỊNH cần anh chọn hướng trước khi làm. Các mục HẠNG MỤC LỚN là việc dựng thêm, cần lên kế hoạch riêng.

ĐÃ SỬA ĐÃ TRẢ LỜI CẦN QUYẾT ĐỊNH HẠNG MỤC LỚN

A. Đã sửa trong phiên này

Những thay đổi rõ ràng, ít rủi ro — đã sửa trong code, sẽ hiển thị sau lần deploy hub kế tiếp.

A1 · Đổi nhãn menu trái mục 3 ĐÃ SỬA

Việc "Người dùng" → "Quản lý tài khoản".
Đã sửa app/templates/hub/base.html, base.html, finance/base.html, review/base.html (4 shell)

A2 · Lỗi gạch nối "an-toàn" ảnh: tab An toàn ĐÃ SỬA

Việc "Danh sách an-toàn", "tín hiệu an-toàn" → bỏ gạch nối thành "an toàn" (tiếng Việt không gạch nối ở đây).
Đã sửa watchlist.html (tiêu đề + H1), cohort.html (banner)

A3 · Tiếng Anh trong cảnh báo an toàn ảnh: bảng watchword ĐÃ SỬA (một phần)

Việc Dòng chi tiết watchword 'danh' matched in note dated… là tiếng Anh và lộ token thô.
Đã sửa Văn bản sinh mới → "Từ khóa cảnh báo «…» xuất hiện trong ghi chú ngày …" tools/safeguarding_watchlist.py:222
⚠ Các dòng cảnh báo đang hiển thị là bản đã lưu trong DB (sinh từ trước). Cần chạy lại quét an toàn trên dữ liệu sống để thay văn bản cũ — xem D4.

A4 · Tiêu đề trang lặp 3 lần "do we need to repeat the header?" ĐÃ SỬA

Việc Mỗi trang hiện tên trang tới 3 nơi: thanh trên (topbar), breadcrumb, và H1 trong nội dung. Topbar lặp lại tiêu đề lớn là thừa.
Đã sửa Bỏ tiêu đề trang lớn ở topbar (3 shell: hub/finance/review) — chỉ còn 1 nhãn khu vực nhỏ làm ngữ cảnh; breadcrumb giữ vị trí, H1 là tiêu đề chính. Khớp với shell care vốn đã làm vậy.

A5 · Đổi tên "Hàng đợi cảm ơn" → "Lời cảm ơn" đề xuất của anh ĐÃ SỬA

Đã sửa finance/home.html:50

B. Câu hỏi — trả lời

B1 · Gửi link thư mục hoạt động cho người upload mục 1 ĐÃ CÓ

Trả lời Có, đã dựng đầy đủ. Khi điều phối viên nộp Form hoạt động, hệ thống tạo thư mục Drive và gửi email kèm link thư mục về cho người nộp để họ tải ảnh lên.
Cài đặt onFormSubmit.gs:140-181 (_sendFolderLinkBack) · cloud/event_folder/app.py:101-102 trả về folder_id
Đã kiểm tra (2026-06-11) Toàn bộ chuỗi mã đã xác minh: Form nộp → Apps Script POST Cloud Run → Cloud Run tạo thư mục và trả về folder_id → Apps Script gửi email qua MailApp kèm link thư mục. Quan trọng: bản chạy thật gửi bằng MailApp của Google (KHÔNG cần SMTP) — đính chính nhận định "chờ SMTP" trước đó (cái đó chỉ đúng với đường Python dự phòng không dùng).
Còn phụ thuộc 2 cài đặt phía Google (không kiểm được từ mã, cần anh xác nhận hoặc test nộp thử): (1) Form ▸ Cài đặt ▸ "Thu thập địa chỉ email" phải BẬT (nếu tắt, sẽ rơi về Script Property NOTIFY_EMAIL; thiếu cả hai thì bỏ qua email, thư mục vẫn tạo). (2) Manifest có scope script.send_mail và đã chạy setupTrigger/cấp quyền lại. Form Khảo sát chưa có link-back (đề xuất nối nốt).

B2 · Dấu "QT" góc trên phải mục 2 ĐÃ TRẢ LỜI

Trả lời "QT" là chữ viết tắt cố định của "Quản trị viên" — một ô avatar đặt cứng trong template base.html:84 (chưa lấy theo người đăng nhập).
Đề xuất: cho ô này lấy chữ cái đầu theo tên người đang đăng nhập (vd "Hiếu" → "H"), tooltip hiện đầy đủ tên + vai trò. Việc nhỏ, làm cùng đợt phân quyền (mục 7). Anh muốn làm không?

C. Cần anh quyết định

C1 · Rút gọn các câu hướng dẫn dài mục 4 (a–i) ĐÃ SỬA

✓ Đã thay đồng loạt theo bảng dưới (anh duyệt 11/06). overview.html, trips.html, survey.html, review/queue.html
#Hiện tạiĐề xuất
aBấm để tới thẳng mục cần xử lý(bỏ — trùng với b)
bCó 2 việc cần bạn xử lý hôm nay. Bấm để tới thẳng mục cần làm.2 việc đang chờ xử lý.
cMọi hoạt động đã ghi nhận, kèm trạng thái lên bài.Toàn bộ hoạt động và trạng thái bài đăng.
d13 hoạt động đã có ảnh nhưng chưa thành bài đăng. Mở một hoạt động để xem brief và Yêu cầu viết bài.13 hoạt động đã có ảnh, chưa lên bài.
eViệc xét chọn được thực hiện ở bước nội bộ riêng; trang này chỉ để xem, chưa xét chọn tại đây.Danh sách ứng viên (xét chọn ở bước riêng).
f1 ứng viên đang chờ xét chọn. Trang này chỉ liệt kê, không hiển thị thông tin nhận dạng (số điện thoại, ngày sinh). Hồ sơ đầy đủ được lưu riêng với quyền truy cập hạn chế.1 ứng viên đang chờ xét chọn. Không hiển thị thông tin nhận dạng.
gKhu vực nội bộ của Quỹ Hoa Mặt Trời.(giữ — đã ngắn gọn)
hMỗi bài hiện gần như bản đăng cuối. Bấm để xem trước, sửa và quyết định.Bản nháp gần với bài đăng cuối. Bấm để xem và duyệt.
Nguyên tắc: phụ đề ≤ 1 dòng, không lặp tiêu đề, không "Bấm để…". Áp dụng cùng tinh thần cho mọi câu tương tự khác.

C2 · Tiếng Anh còn sót trong "Dịp" (Kế hoạch nội dung) ảnh: Bảng 3 tháng ĐÃ SỬA

✓ Đã thêm bộ lọc dip (content_plan.observance_label): children-day → "Ngày Quốc tế Thiếu nhi", back-to-school → "Khai giảng năm học"… Slug lạ thì giữ nguyên. Kiểm tra chạy đúng.
Việc Cột "Dịp" hiện slug tiếng Anh: children-day, family-day, back-to-school.
Cách sửa Thêm bảng ánh xạ slug → nhãn tiếng Việt (Ngày Quốc tế Thiếu nhi, Ngày Gia đình Việt Nam, Tựu trường…) và hiển thị nhãn.
Đề xuất: tôi làm bộ ánh xạ này (việc nhỏ, an toàn). Xác nhận để tôi triển khai.

C3 · Thẻ "Cờ biên tập (tính lúc đọc)" — đã gỡ ảnh: Tổng quan Kế hoạch ĐÃ SỬA

✓ Đã gỡ khối 4 thẻ; thay bằng một dòng tóm tắt gọn "X hoạt động chưa lên bài · Dịp sắp tới…". plan.html

C4 · Từ chối một mục (reject) "what if we want to reject an item?" ĐÃ CÓ SẴN

Đính chính Đã có sẵn, tôi nói nhầm trước đó. Mở một bài trong "Bài đợi duyệt" → cuối trang mục "Quyết định" có đủ 3 nút: Từ chối (đưa vào _rejected, kèm lý do) · Yêu cầu sửa (trả lại người viết) · Duyệt ngay. review/bundle.html:123-128 + decisions_core (duyet/sua/tu-choi)
Còn thiếu Chỉ là khả năng nhìn thấy: từ danh sách hàng đợi phải mở bài mới thấy nút. D1 sẽ đưa các nút này lên gần hơn nếu cần.

D. Hạng mục lớn — cần dựng thêm

D1 · Làm lại bố cục Hàng đợi duyệt mục 6 ĐÃ LÀM

✓ Hàng đợi chuyển sang bảng gọn (ảnh nhỏ · Tiêu đề · Luồng · Cập nhật · Trạng thái · Cảnh báo QC · Thao tác), nhóm theo trạng thái. Thêm route /bundle/{slug}/pack để tải gói đăng (zip) sau khi duyệt; nút "Tải gói đăng" hiện ở hàng đã duyệt và trên trang bài. Cột ngày dùng last_updated (ngày tạo bản nháp riêng là việc nhỏ còn lại). Nút Duyệt/Từ chối giữ ở trang bài (nơi xem được ảnh + xác nhận dignity).
Hiện trạng Lưới thẻ, có hiện ngày (b.date_display) nhưng đó là ngày của sự kiện, không phải ngày tạo bản nháp. app/templates/review/queue.html, app/review_main.py:331
Đề xuất Bố cục bảng gọn, chuyên nghiệp: cột Tiêu đề · Luồng · Ngày bản nháp · Trạng thái · Cảnh báo QC · Thao tác (Xem · Duyệt · Từ chối); nhóm theo trạng thái; ảnh thumbnail nhỏ.
Sau khi duyệt Trả lời câu của anh "duyệt rồi thì sao": bundle chuyển approved → packed; publish_pack.py xuất gói sẵn-đăng (bỏ nhãn DRAFT) để người vận hành đăng tay (Phase A — không tự đăng). Đề xuất hiện rõ bước này trên giao diện sau khi bấm Duyệt ("Đã duyệt → tải gói đăng").
Cần thêm trường ngày tạo bản nháp vào STATUS/registry để hiện đúng "ngày bản nháp".

D2 · Dọn mã học sinh cũ (journey-NNNN) → mã mới (HSYYYYNNNN) mục 5 · "wrong code" ĐÃ LÀM

✓ Bảng cảnh báo an toàn + trang nạp học bạ nay hiện mã học sinh (HSYYYYNNNN), dự phòng journey_id; link giữ journey_id làm khóa. Rà soát journey-3007: đây là học sinh thật (HS20250032, THCS Đại An, lứa 2025) — khối journey-2xxx/3xxx là khối đánh số theo trường/lứa (Vĩnh Hào 2024 / Đại An 2025), không phải rác. Kiểm tra dữ liệu sống: 0/142 em thiếu mã nên không cần backfill.
Việc Nhiều bảng giao diện vẫn hiện mã nội bộ journey-3007, journey-0021 thay vì mã học sinh mới HSYYYYNNNN. Bảng cảnh báo an toàn ("Mã hồ sơ") là ví dụ rõ.
Cấu trúc journey_id là khóa nội bộ (giữ nguyên, không đổi); student_code (HS+năm+số) là mã hiển thị cho người. beneficiary_registry_migrate.py:275
Cách sửa (1) Mọi bảng/đầu trang hướng người dùng hiển thị student_code, dự phòng journey_id nếu trống. (2) Rà các mã lạ kiểu journey-3007 (block 3000+ bất thường) xem có phải dữ liệu rác. (3) Backfill cho em nào còn thiếu student_code.
Điểm cần sửa: watchlist.html:20 (đang dùng item.journey_id) + đường đọc; child.html/cohort.html đã có dự phòng. Cần chỉnh truy vấn để lấy student_code.

D3 · Tab "Phân tích" — cấu trúc lại + biểu đồ "more meaningful data, charts?" ĐÃ LÀM

✓ Thêm thẻ chỉ số (tổng em · đang hỗ trợ + % · tín hiệu an toàn mở · tổng phúc lợi) và biểu đồ nhẹ (thanh ngang cho trường & 8 cánh, đường SVG theo tháng + lũy kế) — không thêm thư viện. Hàng "admin (none)" đổi thành "Chưa phân loại" và xếp cuối (sửa luôn ở petal_label cấp phân tích).
Hiện trạng Ba bảng số thô (HS theo trường · Phủ 8 cánh · Phúc lợi theo tháng). Có lỗi dữ liệu: hàng "admin (none)" trong bảng 8 cánh là rác phân loại (note không gắn cánh → rơi vào author "admin").
Đề xuất Tổ chức lại theo cụm có ý nghĩa, kèm biểu đồ nhẹ (SVG, không thêm thư viện nặng): Quân số theo trường (cột) Phủ 8 cánh (thanh ngang) Phúc lợi theo tháng (đường + tổng lũy kế) Tỉ lệ đang hỗ trợ Tín hiệu an toàn đang mở
Sửa kèm Lọc bỏ hàng "admin (none)"; gộp note không gắn cánh thành "Chưa phân loại" và đặt cuối.
Hỏi: anh ưu tiên chỉ số nào nhất? (vd: phủ 8 cánh, xu hướng phúc lợi, hay tỉ lệ hỗ trợ theo trường)

D4 · Cảnh báo an toàn sai ngữ cảnh: "danh", "đói/đời" mục 8 · "danh" ĐÃ LÀM (12/06)

Đã làm 12/06/2026 trên dữ liệu sống (bucket care) theo quy trình tránh race gcsfuse: quiesce hub+care → sao lưu (care/_backups/20260612-pre-d4/) → tải DB → xoá 12 dòng cảnh báo từ-khóa cũ → chạy lại bộ so khớp hiện tại (run_watchlist daily + persist_signals) → checkpoint WAL → upload → kiểm tra lại (giữ nguyên qua 2 lần đọc). Kết quả: 12 → 3 cảnh báo, đều văn bản tiếng Việt: journey-0021 «withdrawn» (ghi chú nghỉ học thật), journey-3007 + journey-3026 «tự ti» (bài viết trưởng thành — trường hợp lành tính, để điều phối viên xem, không tự ý ẩn). Dương tính giả 'danh'/'đói/đời' đã hết. Số học sinh = 142 không đổi. Cron care-watchlist-keyword nay chạy image đã sửa nên không tái nhiễm.
Việc Giao diện hiện cảnh báo từ khóa 'danh' — là dương tính giả (danh sách / danh dự…), và 'withdrawn' (tiếng Anh).
Chẩn đoán Bộ so khớp trong code đã giữ dấu thanh + biên từ + danh sách ngoại lệ ("đánh giá"…) từ bản sửa 10/06. Nhưng các dòng cảnh báo đang hiện là bản lưu cũ trong DB sống, sinh từ trước bản sửa; và bộ từ khóa sống có thể còn từ tiếng Anh (hit, hungry, withdrawn…).
Cách sửa (1) Chạy lại safeguarding_watchlist trên dữ liệu sống bằng code+config hiện tại để xóa cảnh báo "danh" giả và cập nhật văn bản tiếng Việt. (2) Rà tu-khoa-canh-bao.yaml sống: bỏ các từ tiếng Anh (ghi chú đều tiếng Việt) hoặc cho hiển thị nhãn Việt. (3) Thêm ngoại lệ cho "đôi/đợi/theo dõi/đời" nếu sau này thêm từ khóa liên quan.
Đây là thao tác trên dữ liệu sống (bucket care) — làm có kiểm soát, sao lưu trước, một tiến trình tại một thời điểm (race gcsfuse). Cần anh bật đèn xanh.

D5 · Giao diện module "Quản lý học sinh" chưa căn chỉnh/đồng bộ "not aligned · UI/flow not good" ĐÃ LÀM (phần chính)

An toàn (watchlist) restyle đầy đủ: page-head + nhãn loại tín hiệu tiếng Việt + bảng .data + form xử lý gọn. Danh sách em: page-head, bộ lọc gọn lại (không tràn), thẻ nhóm dùng token. Phân tích đã làm ở D3. Trang Hồ sơ em vốn đã dùng .data/page-head — để lại, tinh chỉnh thêm nếu cần.
Việc Các trang care (Danh sách em, An toàn, Phân tích, Hồ sơ em) dùng shell/template (app/templates/base.html + bảng HTML thô), nên lệch cột, chữ tràn dòng ("journey-/0021"), trông kém chỉn chu so với shell hub mới.
Đề xuất Đưa các trang care theo cùng hệ thiết kế của shell hub: bảng có căn cột, không tràn mã, khoảng cách nhất quán, bộ lọc gọn. Làm theo từng trang.
Đây là việc restyle có khối lượng; nên gộp chung với D1/D3 thành một đợt "đồng bộ giao diện care".

D6 · Phân quyền Drive theo tài khoản + quy trình tài khoản chuyên nghiệp mục 7 ĐÃ LÊN SUB-PLAN

✓ Đã viết sub-plan riêng (_system/plans/hub-access-accounts-subplan.md): hiện trạng (tài khoản + phân quyền module đã có; chưa có email/mời/quên mật khẩu, chưa có cấp quyền Drive) + 2 quyết định then chốt cần anh chọn + lộ trình P0–P5. Cần anh quyết định trước khi code (xem mục dưới).
Mục tiêu (a) File trong HMT Workspace chỉ-đọc với mọi người trừ admin để giữ nguồn-sự-thật; sửa phải qua admin duyệt rồi mới phản chiếu vào bucket. (b) Cấp quyền thư mục Drive cho user theo phân quyền trong hub khi tạo tài khoản. (c) Tạo tài khoản + đăng nhập lần đầu chuyên nghiệp: email mời, tự đặt mật khẩu, quên mật khẩu.
Hiện trạng Hub đã có tài khoản theo người + phân quyền module (care/content/finance) care_auth.py. Chưa có: liên kết quyền hub ↔ quyền Drive (không có lệnh gọi permissions().create nào); và chưa có luồng đặt mật khẩu lần đầu / quên mật khẩu / email mời — user hiện được admin đặt sẵn mật khẩu.
Đề xuất khung 1 · Khóa Drive về chỉ-đọc (trừ SA + admin) 2 · Map module hub → thư mục Drive 3 · Khi tạo user: SA cấp quyền reader đúng thư mục theo module 4 · Email mời + token đặt mật khẩu lần đầu 5 · Quên mật khẩu qua email
Quyết định cần có: (i) gửi email qua đâu (bật SMTP — trùng với B1)? (ii) user truy cập Drive bằng tài khoản Google riêng hay chỉ xem nội dung qua hub? Điều này đổi hẳn cách cấp quyền. Đề xuất tôi viết một sub-plan riêng cho mục 7 trước khi code.

D7 · Trang xem sao kê ngân hàng + xem trước/tải ở panel phải yêu cầu mới ĐÃ LÀM

✓ Thêm tab "Sao kê" trong Tài trợ & tài chính: bảng tệp (tên · loại · kích thước · ngày), panel trượt phải xem trước PDF (iframe) / ảnh, nút Tải về, đóng bằng ESC hoặc nền mờ. Route tải có chặn path-traversal. Bật bằng HMT_FINANCE_STATEMENTS_DIR — hiện để trống → trạng thái rỗng gọn; sẽ hiện dữ liệu khi band 4_Tai-chinh (đường email→Drive) được gắn vào hub.
Mong muốn Một nơi liệt kê các sao kê/chứng từ ngân hàng liên quan, bấm vào để xem trước + tảipanel trượt ra bên phải, đóng bằng phím ESC.
Đề xuất Thêm tab mới "Sao kê" trong khu Tài trợ & tài chính (anh gợi ý để riêng một tab — hợp lý, không nhồi vào Tổng quan). Danh sách chứng từ kéo từ Drive (đường thu thập email→Drive đã có sẵn — xem ghi chú dưới). Panel phải xem trước PDF/ảnh, nút Tải, đóng bằng ESC hoặc bấm nền mờ.
Tận dụng: đường "bank-notification email lane" đã lưu chứng từ vào Shared Drive (tools/bank_notification_extract.py + cloud/finance_mail). Cần nối phần hiển thị + panel xem trước. Việc dựng vừa, có frontend panel + đọc danh sách file Drive.

D8 · Làm lại bảng "Danh sách tài khoản" — trông rất tệ "looks so bad" ĐÃ LÀM

✓ Bảng nay chỉ hiển thị (Tài khoản · Vai trò · Khu vực dạng chip · Trạng thái); mỗi dòng có nút "Quản lý" mở panel trượt phải để đổi quyền · đặt lại mật khẩu · khoá/mở (có chặn tự khoá). "Thêm tài khoản" chuyển vào panel riêng. Đóng bằng ESC/nền mờ. Luồng mời/quên mật khẩu chuyên nghiệp nằm ở D6.
Việc Bảng quản lý tài khoản hiện lộn xộn: ô nhập "Mật khẩu mới" nội tuyến với khối đỏ, checkbox quyền chen trong ô, nút Khoá/Đặt lại xếp chồng — thiếu căn chỉnh, kém chuyên nghiệp.
Đề xuất Tách thao tác ra khỏi bảng: bảng chỉ hiển thị (Tài khoản · Vai trò · Khu vực · Trạng thái), mỗi dòng có một nút "Quản lý" mở panel/biểu mẫu riêng để đổi quyền · đặt lại mật khẩu · khoá. Quyền hiển thị dạng chip gọn, không phải checkbox rời. Gắn với luồng tài khoản chuyên nghiệp ở D6 (mời qua email, tự đặt mật khẩu lần đầu, quên mật khẩu) thay cho ô "đặt mật khẩu" nội tuyến hiện tại.
Đây là phần giao diện của mục 7 / D6 — nên gộp làm cùng.
Nguồn-sự-thật trạng thái build vẫn ở plan-tracker. Trang này là punch-list rà soát, không phải tài liệu thiết kế khóa.
Phase A nguyên vẹn: không gì rời workspace mà chưa /approve. · Cập nhật 2026-06-11.