Changelog

All notable changes to the Tân Doanh Build PC plugin, newest first.

PC BuilderUpdated 21 Oct 2026

CHANGELOG - Tân Doanh Build PC


[2.10.5] - 2026-10-06

Fixed - Toast notification position (BP-11 / BR-07)

  • Desktop (>= 768px): the toast is anchored to the BOTTOM-RIGHT corner (24px from the bottom edge, leaving 96px on the right for the floating chat button) instead of right under the sticky header, so it no longer covers the breadcrumb area at the top of the page (even with the admin bar). The old top: var(--tdbp-toast-top) line is overridden in td-a16.css.
  • Mobile (<= 767px): the toast stays above the floating total bar (it no longer covers the title and the close X button of the installment calculator modal), with an extra 80px on the right so it does not cover the floating chat button.
  • Rebuilt td-a16.min.css.

[2.10.4] - 2026-10-06

Fixed - BR-11: deadlock when saving a session (table wp_tandoanh_buildpc_sessions)

  • Root cause: save_session() ran START TRANSACTION; SELECT ... FOR UPDATE; INSERT on a row that does NOT exist yet, and session_id was only a plain KEY: two simultaneous requests (a new customer's first selection fires add_product + save_session for the same session, or two new customers) held gap locks on each other and InnoDB killed one request (error 1213).
  • A one-time migration (TanDoanhBuildPC_Database::maybe_upgrade_sessions_schema(), option flag tandoanh_buildpc_sessions_uq, guarded by GET_LOCK): removes duplicate (session_id, builder_id) rows keeping the one with the newest updated_at, then adds UNIQUE KEY uq_session_builder (session_id, builder_id). It runs automatically on plugin upgrade (including on production), is safe to repeat; on failure it does not set the flag and uses the old save path.
  • save_session() uses a single INSERT ... ON DUPLICATE KEY UPDATE statement (no SELECT FOR UPDATE, no gap lock) once the UNIQUE key exists; it retries once on error 1213/1205. The read-only share rule is kept: the share_* row is checked by data with a plain SELECT plus an IF(session_id LIKE 'share\_%' ...) condition in the UPDATE part; only share.php (flag $allow_share_write) may create or write share_* rows.

Fixed - UI (review 70)

  • BR-01/02: row grid changed: component column 164, image 56, qty 100, price 108 (gap 12) so the name column is 322px at 1440/1920 and >= 242px from 1320; the 1101-1319px range uses two-tier rows (the name is no longer squeezed to 0px and the Delete button is no longer cut off).
  • BR-03: at <= 380px the empty-row button shows only "+ Chọn" (+ Choose), and the step label is no longer covered by the button. BR-04: touch targets >= 40px ("+ Chọn X", -/+, "Xóa tất cả" (Clear all), "Tính trả góp" (Calculate installments) in the collapsed bar has a 40px-high row, the bar toggle button is 40x40; the mobile floating bar is 130px = 15.4% of an 844px screen). BR-05: mobile group chips become a 2-column grid (no edge clipping). BR-06: 12px gap between the icon and the step label in two-tier rows. BR-07: the mobile toast sits above the total bar (no longer covers the modal). BR-09: dark mode no longer shows a dark rectangular frame around text. BR-10: the hotline number in the print page notes is lowercase text.

Changed - BR-08: the component picker popup filters MULTIPLE values per group

  • Clicking a chip adds or removes it from the group (OR within a group, like the category page); the hidden select is multiple; one value sends a string, several values send an array (the server already supported IN); selected filter chips appear at the top of the list, one chip per value, and clicking X removes exactly that value.

[2.10.3] - 2026-10-06

Changed - A4 quote print page (templates/print-sheet.php, print.php, print-sheet.css)

  • Company header: line 1 "Tân Doanh" (bold, large like the old company name), line 2 "Công ty TNHH TH Hoàng Phương" (regular, smaller). Split at display time from td_biz('company.name') (dropping the " - " suffix), WITHOUT changing the business info option. Address, hotline, email and website are unchanged.
  • The "Thông tin khách hàng" (Customer information) block (full name, phone, notes) and the 2 signature boxes (Customer, Representative) are printed ONLY when the person printing is a logged-in staff member (edit_shop_orders or manage_woocommerce). Ordinary or logged-out visitors: both are hidden and the footer layout shrinks (QR + notes) with no empty gap. The component table, totals, installments, QR and general notes still show.
  • The print page is never cached (DONOTCACHEPAGE + nocache_headers, Cache-Control: no-store), so the staff and customer versions are never mixed.

[2.10.2] - 2026-10-06

Fixed - Power supply wattage estimate when NO power supply is chosen (reported: with CPU + VGA chosen it still said "Chọn CPU, VGA và nguồn để ước tính" (Choose CPU, VGA and power supply to estimate))

  • includes/compat.php: analyze() computes need (= (CPU + VGA + 75W system) x 1.4, multiplied by quantity, same formula as 2.9.5) and recommend (rounded up to the steps 450/500/550/600/650/750/850/1000/1200...) as soon as a CPU and/or VGA is present, WITHOUT depending on a power supply being chosen. Added psu_status = 'none', has_cpu, has_gpu. When a power supply is present, the sufficient/insufficient comparison and "dư xW" (x W spare) are unchanged. psu_watts() also reads wattage written right after the PSU series code (SF750, CX650, TX700).
  • Total column: a line "Cấu hình cần khoảng ~NNNW" (The build needs about ~NNNW) + "CPU ~xW + VGA ~yW + hệ thống 75W, dự phòng 1,4. Gợi ý nguồn từ ZZZW" (CPU ~xW + VGA ~yW + 75W system, 1.4 margin. Suggested power supply from ZZZW); with only a CPU it adds "Chưa tính card đồ hoạ" (Graphics card not counted) (only a VGA: "Chưa tính CPU" (CPU not counted)); a grey wattage bar "cần ~NNNW / gợi ý ZZZW" (needs ~NNNW / suggested ZZZW) (not yet compared against any power supply). With neither CPU nor VGA: "Chọn CPU hoặc card đồ hoạ để ước tính công suất nguồn" (Choose a CPU or graphics card to estimate power supply wattage).
  • Power supply picker popup: each power supply has a label "Đủ công suất (cần ~NNNW)" (Enough wattage (needs ~NNNW)) or "Có thể thiếu, cấu hình cần ~NNNW" (May be insufficient, the build needs ~NNNW) (the card carries data-psu-w, and the label is attached in the browser so the cached list HTML stays correct). It only marks items; it does not hide them, change the order or break pagination.

Changed - The installment line in the total column no longer names a bank

  • Cause of the "Trả góp Nam A Bank 12 tháng" (Installment Nam A Bank 12 months) line: the theme's installment modal automatically picks the lowest-fee bank for the 12-month term when OPENED and writes it to sessionStorage tdInstallment.last; Build PC read that key and treated it as "the customer chose it". It is not a site-wide localStorage, but it was an automatic choice.
  • It now shows "Góp từ X đ/tháng" (Pay from X VND/month) by default, plus a small line "Ước tính kỳ hạn 12 tháng, đã gồm phí. Số tiền thực tế tùy đơn vị trả góp và kỳ hạn bạn chọn." (Estimate for a 12-month term, fees included. The actual amount depends on the installment provider and the term you choose.) (the lowest eligible fee, calculated the same way as the product card, without naming a provider). Only when the customer ACTIVELY interacts in the modal and then closes it is the choice saved (tab sessionStorage, separate key tdbp.inst.pick) and "Đã chọn: <đơn vị> <kỳ> tháng - X đ/tháng" (Selected: months - X VND/month) + Đổi (Change) + Bỏ chọn (Clear selection) is shown. If the total drops to 0 or the choice is no longer valid, it is dropped automatically. When the total is below the minimum: the "Góp từ" line is hidden and the "Tính trả góp" button stays.
  • Print page: if a choice exists, the plan for exactly that provider is printed; if not, a "Trả góp tham khảo" (Reference installments) table is printed (monthly payment from, by term, without bank names) using tandoanh_installment_from_table() from theme 1.9.52.

Changed - Popup filters: keep all values in the group being selected

  • class-ajax.php (get_step_filters, compute_filters_data): for a group that already has selected values, the available values of that group are recomputed with every OTHER condition (ignoring that group's own condition), so choosing AMD still leaves INTEL available to switch to; other groups still narrow according to the filter. The filter cache key gains v.

Changed - Mobile

  • Floating total bar 136px -> 124px (14.7% of an 844px screen). Step icon 40px (previously forced to 52px by the theme, which covered the label), type label 6px from the image.

[2.10.1] - 2026-10-06

Changed - Lighter, more spacious A4 quote print page (templates/print-sheet.php, assets/css/print-sheet.css)

  • Reduced boldness: the table body (component name, quantity, unit price, line total, warranty), amount in words, notes and the installment plan go to weight 400; the group label "CPU - VI XỬ LÝ" is weight 500, 7.3pt, dark grey; the SKU is light grey weight 400. Only these stay bold: the heading "BÁO GIÁ CẤU HÌNH MÁY TÍNH" (PC CONFIGURATION QUOTE), TỔNG CỘNG (TOTAL), the monthly payment of the selected term, the company name and section headings. Fixed .tdp b (600) overriding .tdp-name that kept component names bold. The self-hosted Inter/Be Vietnam Pro fonts have real weights (widths at 400/500/600/700 measured as different), with no faux bold.
  • More air: body text 10.5pt (table 10pt), line-height 1.45, table row padding 2.3mm, thin #e6e6ea row lines instead of alternating backgrounds, 5-6mm between blocks, product images 10mm, a sparser term comparison table (the selected term is still highlighted).
  • Two A4 pages: page 1 holds the header, customer info, component table, total and amount in words; when an installment is chosen, the installment plan + comparison table + QR + notes + 2 signature boxes go to page 2 (page break through the .tdp-pb element). With no installment, the QR, notes and signatures sit at the bottom of page 1 (an 8-item build still fits on 1 page). Builds with many items: the table flows onto the next page, repeats its header and never splits a row. "Trang x / y" (Page x / y) is correct.
  • Content and data are unchanged. Technical note: the same @page margin is used for all pages (the first page has a top margin of 0 so the yellow band touches the edge); a separate repeated header for page 2 was tried and dropped because Chrome drew the upper half of accented letters at the bottom of page 1.

[2.10.0] - 2026-10-06

Changed - New Build PC page UI (option A "wide configuration table")

  • Removed the black "TÂN DOANH PC BUILDER" banner. Replaced by a breadcrumb (the theme's .woocommerce-breadcrumb component), an H1 "Tự chọn cấu hình PC" (Build your own PC) (only on the page with slug tu-chon-cau-hinh-pc; elsewhere the builder name is kept), one description line and 2 chips "Kiểm tra tương thích tự động" (Automatic compatibility check) and "Báo giá A4" (A4 quote). The filter tandoanh_buildpc_heading can change the heading.
  • Wide frame using the header container (1352px at 1440+), total column 392px. Theme: removed the max-width: 1060px limit on .buildpc-content (page-tu-chon-cau-hinh-pc.css and the purged copy).
  • The 11 steps become one table split into 4 groups (Core, Storage and power, Case and cooling, Display) with an x/y count per group; an empty row is a compact single line with a "+ Chọn X" button; a selected row has a 64px image, a 2-line name, quantity, unit price, and 40px Đổi (Change) / Xóa (Delete) buttons (icons, aria-label kept). The line "Thành tiền: ..." (Line total: ...) shows only when quantity > 1. display: contents is used on the old blocks so the DOM, classes and JS (session, Change, Delete, quantity, share, preset) stay unchanged.
  • The 11 step icons switch to a consistent line-style SVG set in a yellow tile (custom steps whose type is not recognised still use the icon/image set by the admin).
  • Total column: Total 32px, "Tiết kiệm x đ so với giá niêm yết" (You save x VND versus list price) (only when the total list price exceeds the total selling price, real data from the server), progress x/11 components, a checklist (socket, RAM, power supply) with pass/fail/insufficient-data status, a wattage bar "NNNW / MMMW, dư/thiếu xW" (NNNW / MMMW, x W spare/short), a line "Trả góp qua thẻ tín dụng hoặc công ty tài chính" (Installments by credit card or finance company) + the "Tính trả góp" button (under the total as in spec A2), Thêm vào giỏ hàng (Add to cart), Xóa tất cả (Clear all), Chia sẻ / Gửi email / In-PDF (Share / Send email / Print-PDF).
  • Mobile (<= 767px): the floating total bar keeps the fixed + expand behaviour (136px high = 16.2% of an 844px screen), the detail block only shows when expanded; a progress chip strip for the first 4 groups at the top (tap to jump to the group); component rows are two-tier (<= 900px).
  • Tablet 768-1100: 1 column, the total column sits statically below the table.

Changed - Filters in the component picker popup match the category page

  • Each filter group: an SVG icon (the theme icon registry, passed through tandoanh_buildpc_ajax.icons), an UPPERCASE title with a thin underline, a collapse/expand control (<details open>), values as 32px chips (a selected chip has a yellow background), at most 250px scrolling inside the group with a thin scrollbar. Groups with more than 20 values get a small search box. select2 was removed from the filters.
  • <select class="attribute-filter"> is still the (hidden) source of truth: chips only set the select's value and then fire change, so AJAX parameters, dynamic filtering, clearing filters and pagination are unchanged. Chips are rebuilt when the select changes (FilterManager.syncChips).
  • A pill sort bar (Bán chạy (Best selling), Mới nhất (Newest), Giá thấp → cao (Price low to high), Giá cao → thấp (Price high to low), Đánh giá (Rating), Tên A-Z (Name A-Z)) + a "N sản phẩm" (N products) count (from found_posts). The "Xóa tất cả bộ lọc" (Clear all filters) button only shows when a filter is active. Mobile keeps the filter sheet sliding up from the bottom with the same chip look.

Added

  • includes/compat.php: analyze() additionally returns sale_total, regular_total, save (the old keys are unchanged).
  • assets/css/components/td-a16.css (+ .min).

[2.9.5] - 2026-10-06

Fixed

  • The power supply wattage warning now adds up by quantity: graphics card (VGA) x quantity, CPU x quantity, and fully adds up when a step has several products (previously it counted only 1 and ignored quantity). The warning text states "card đồ hoạ x2 ~NNNW" (graphics card x2 ~NNNW) when there is more than one. RAM/SSD/fans do not have their own factor yet (they sit within the 75W system share) so they are unchanged. The client only displays the server result so both sides always match; the compatibility check endpoint now accepts quantity (capped at 20 per row).
  • Several tabs in one session: when a tab is returned to (visibilitychange, focus, or restored from the browser back/forward cache) it compares with the server; if another tab has changed or used "Xóa tất cả" (Clear all), the new state is loaded before the user acts, preventing a stale tab from overwriting. No DB structure change.

[2.9.4] - 2026-10-06

Changed

  • The collapsed total bar on mobile (<= 767px) keeps its entry to installments: the "Tính trả góp" button shrinks to a small link (card icon + 13.5px text, brand yellow) on the right of the "Tạm tính" (Subtotal) line, opening the exact installment modal with the current total. No extra row is added so the bar is still about 136px high (about 16% of an 844px screen); a 40px touch area through ::after. The "Trả góp ... Đổi" line only shows when the bar is expanded.
  • Modal at 768-1023px: the filter column shrinks from 272px to 230px so the grid is wider, and product names in the grid take at most 2 lines. Other sizes unchanged.

[2.9.3] - 2026-10-06

Fixed - Build PC audit fixes (BP-01..BP-18, the plugin's part)

  • The "Thêm vào giỏ hàng" (Add to cart) and "Xóa tất cả" (Clear all) buttons are no longer squashed (23px / 21px) on small screens: removed flex: 1 in the flex column (mobile.css) and added flex: 0 0 auto in td-ui-v2.css. The main button is 46-48px and "Xóa tất cả" is 40px on touch devices.
  • One breakpoint for the floating total bar: 767px (same as the theme and td-ui-v2.css). From 768 up the bar sits statically in the page like the 1-column layout and no longer covers 36% of a tablet screen. The collapsed floating bar now only holds Total + Add to cart (the installment block shows when expanded with the arrow button), taking about 16% of the screen height instead of 26%.
  • The selected-installment line: removed the stray "-" before the Đổi (Change) button (buildpc-installment.js).
  • The floating chat button (#td-fab) sits above the floating total bar (bottom changes with --tdbp-bar-h, without touching transform) so it no longer covers the Change / Share / Print buttons.
  • The "Chưa chọn sản phẩm nào" (No product selected yet) box and selected component rows no longer overflow 9px to the right (width auto + border-box).
  • Filter groups have an even 62px height (native select and select2); the hidden select of select2 no longer takes up a grid row.
  • Minimum 40px touch targets for the List/Grid buttons, the -/+ quantity buttons, the filter chip remove button, the total bar collapse button and the installment Change button (hit area enlarged, icon not scaled up).
  • Products per page in the modal 16 -> 20 (divisible by the 4 and 5 column grids). An old setting of 16 switches to 20 once on upgrade (other values are kept); the endpoint accepts per_page 12/20/24/48; changing the setting in admin refreshes the endpoint's Redis response cache automatically.
  • Modal 768-1023: the Sort box fills the row. Grid: the selling price is always aligned between cards with and without a list price; the Choose button no longer overlaps the price at 768-820 (actions are no longer absolute in the grid style).
  • The desktop toast notification sits below the sticky header (position measured by JS, variable --tdbp-toast-top), with a dark background and yellow border instead of green/blue.
  • Desktop pagination: the disabled Previous/Next buttons keep their space (visibility) so the page number group does not jump sideways.
  • "Xóa tất cả", "Thêm vào giỏ" and "Xóa sản phẩm" use a confirmation box with the same style (role=alertdialog, Esc/Cancel, focus returned) instead of window.confirm; "Chưa có sản phẩm nào" (No products yet) uses a toast instead of alert. Shared function TanDoanhBuildPC.confirmDialog (ui-v2.js).
  • Console: the quantity input has name="qty", the select2 search box has name + aria-label (no more warnings about form fields missing id/name).
  • Select2 in dark mode: dark list background, items without a bright border, scrollbar following dark tokens.
  • Build: mobile.min.css and mobile.min.purged.css (the version the tandoanh theme actually loads via purged_css) were rebuilt from mobile.css.
  • Not fixed (belongs to the theme or awaits approval): BP-07 keep select2 with a search box (already decided), BP-15 small header/footer text in the theme, BP-18 builder block width set by the theme.

[2.9.2] - 2026-10-06

Fixed - UI review fixes for 2.9.x

  • The List/Grid toggle: at every size it always has the correct aria-pressed (true/false), both buttons always show, and the selected one has a brand background (previously mobile removed aria-pressed and CSS hid it by mistake).
  • Grid by viewport: 2 columns below 768, 3 columns at 768-1023, 4 columns at 1024-1279, 5 columns from 1280.
  • Toolbar: the search box is at least 200px and wraps to a new row when tight (768); on mobile the search box takes the whole first row, with the full placeholder.
  • The component row markup of the share/landing path (share.php) is aligned with the new selection path: Change/Delete buttons type="button", aria-label including the product name, aria-hidden icons, a quantity input with aria-label; the JS fallback (product-manager.js) also includes the name.
  • The mobile toast notification sits below the header (no overlap); the "Khách hàng đã mua" (Customers have bought) popup of the woo-notifications module is hidden on the mobile Build PC page.
  • session-manager.js: sessionStorage wrapped in try/catch, with an in-memory variable fallback (the modal still opens when storage is blocked).
  • Added td-ui-v2.min.css (csso) to the build script; the plugin uses it automatically when it is newer than the source.

[2.9.1] - 2026-10-06

Changed - A4 print page redesigned as a configuration quote sheet

  • Header: a full-bleed brand yellow band + ink rule, a logo on a transparent background (no more black box), company name, address, hotline, email, website (the host of home_url(), which becomes tandoanh.vn automatically on production), and on the right the title "BÁO GIÁ CẤU HÌNH MÁY TÍNH" (PC CONFIGURATION QUOTE), the configuration code and the print date.
  • The "Thông tin khách hàng" (Customer information) block has ruled lines to fill in by hand (Full name, Phone number, Notes), left blank.
  • Component table: columns No., Image 44px, Component (accent-colored type label, SKU on the same line as the label, bold name), Warranty (only when present), Qty, Unit price, Line total; alternating light rows, right-aligned tabular-nums numbers, no breaks inside a row, the table header repeats on page breaks.
  • Totals block: Subtotal, Total and an amount-in-words line (TanDoanhBuildPC_Print::money_words(), Vietnamese reading rules for mười/mốt/lăm/tư/lẻ, up to trillions).
  • The installment plan is a compact light card: a summary table (provider, term, down payment, monthly payment highlighted, total fees or interest, total payable), a horizontal term comparison table (the selected term has a bold border so it is still clear in black-and-white print), a fee/payment schedule line and a disclaimer line. An 8-item build with installments fits on 1 A4 page.
  • One-row footer: a QR "Quét để xem và chỉnh cấu hình" (Scan to view and edit the configuration), general notes (prices change over time, call the hotline to confirm), 2 signature boxes "Khách hàng" (Customer) and "Đại diện Tân Doanh" (Tân Doanh representative); page number "Trang x / y".
  • On screen: an A4 sheet on a grey background, the "In / Lưu PDF" (Print / Save PDF) and "Quay lại Build PC" (Back to Build PC) button bar is hidden when printing; the phone preview does not overflow horizontally.
  • Files: includes/print.php (number-reading function + words, brand data), includes/templates/print-sheet.php, assets/css/print-sheet.css. Figures, installment calculation and security (noindex, no-store, inst parameter check) are unchanged.

[2.9.0] - 2026-10-06

Changed - Component picker, filter and total block UI (no change to filter logic, parameters sent to the server, or cache keys)

  • The component picker modal is centred on screen (fixed overlay, height min(100%, 900px), max width 1280px, above the site header, body scroll locked), and full-screen on mobile. Yellow header, 44px close button. Only the list area (and the filter column when needed) scrolls, with no nested scrollbars.
  • Desktop filters: a 272px left column, labelled groups, all controls in the same style (native select and select2 both 40px high, 10px radius, same chevron), the "Xóa tất cả bộ lọc" (Clear all filters) button as a brand secondary button.
  • Mobile filters: a one-row toolbar (search, "Bộ lọc (n)" (Filters (n)), Grid/List switch, an icon-style sort), the filters are a sheet sliding up from the bottom (role=dialog, Esc closes the sheet first and then the modal, focus trap, "Xoá lọc" (Clear filters) and "Áp dụng" (Apply) buttons pinned to the bottom), and selected filter chips have a remove button. Mobile uses the browser's native select (filter-manager.js does not initialise select2 at <= 767px).
  • The product list defaults to LIST view (88px image, 2-line name, struck-through list price + selling price, a quantity control + Choose button on the right); a Grid/List toggle (aria-pressed, remembered in localStorage tdbp_view). Grid: 5/4/3/2 columns depending on the width of the main frame.
  • Root-cause fix for blurry images: the product card loaded the thumbnail size (100px) and CSS scaled it up to about 250px. Now src = woocommerce_thumbnail (300px) with a srcset of the available sizes <= 600px and sizes per display style (ui-v2.js), so images are never scaled beyond the real file (class-ajax.php bp_card_image()).
  • Brand pagination: 10px radius buttons 40px high, the current page with a gold background and no shadow, Previous/Next buttons with SVG chevrons, aria-current, and on mobile a shortened ‹ 1 / 8 ›.
  • Selected component rows: the root cause of the "2 red and black squares" was that the Edit/Delete buttons used <i class="fas fa-pen|fa-trash"> but fa-mini.css (2.3.1) only has 5 icons, so only solid backgrounds remained. Added the pen, trash and image icons to fa-mini.css; the Change / Delete buttons have an icon + label, Delete in a soft danger style; 40px -/+ quantity control; 2-line name, brand-colored price; the step row (icon, name, + button) no longer wraps on mobile.
  • Total block: "Tổng cộng" (Total) in tabular numbers, a line "Tạm tính · n linh kiện" (Subtotal · n components), the "Tính trả góp" button as a compact secondary button, the selected-installment line becomes a chip with a Change button, "Thêm vào giỏ hàng" in gold without a red shadow, "Xóa tất cả" as a link, secondary actions with icon + text (Share, Send email, Print / Save PDF; Print and PDF merged because both open the print page data-tdbp-act="print"), and compatibility warnings compact to 2 lines when the mobile floating bar is collapsed.
  • New files: assets/css/components/td-ui-v2.css (loaded last), assets/js/ui-v2.js (+ .min.js). Changed: class-frontend.php (toolbar and total block markup), class-ajax.php (card image), modules/modal-manager.js (pagination, Esc/Tab per sheet), modules/filter-manager.js, fa-mini.css. The print page files were not changed.

[2.8.3] - 2026-10-06

Fixed - Old customer sessions lost after 2.7.0

  • 2,323 old sessions (2,057 of the form a|b, 266 of the form tandoanh_buildpc_x.y; all 846 rows that had components) were no longer reachable because the client_session normalisation accepted only a-z0-9_-. The whitelist now adds . and | (still lowercase, 1-64 characters, no other characters; share_* is still blocked by string and by DB data as in 2.8.2).
  • session-manager.js generateSessionId() generates ids with only a-z0-9 and - for new sessions. Rebuilt session-manager.min.js.

[2.8.2] - 2026-10-06

Fixed - Bugs from the final review of batch A2 + B + C

  • R-F-01 (security): the read-only share link could be bypassed with SHARE_ / Share_ because the session_id column is case-insensitive while the prefix check was case-sensitive. client_session is now normalised at the very start of every handler (lowercase, only a-z0-9_-, 1-64 characters; TanDoanhBuildPC_Database::normalize_post_client_session()), the share_ prefix check is case-insensitive in class-ajax.php, share.php and class-database.php, and save_session() checks write permission by DATA: if the row currently in the DB is a shared copy (matched by collation) the write is refused, even when the submitted string does not start with share_.
  • R-F-02: the response cache key of the fast endpoint (buildpc-endpoint.php) lacked per_page, so a single anonymous request could corrupt the cache for other customers; the key now includes per_page, on_sale, price_min, price_max, rating, attribute_key/value, _override_selected_products along with the old parameters. per_page from the client accepts only 12 / 24 / 48, other values use the step setting. Also fixed the "Array to string" warning when attributes or filter_* is an array.
  • R-F-05: at mobile 390, a selected product row no longer overflows horizontally (quantity box fixed at 60px, min-width:0 for the row); the floating chat button is pushed above the floating total bar (--tdbp-bar-h written by compat-check.js through a ResizeObserver, CSS in td-fixes.css), so it no longer covers the "Thêm giỏ hàng" (Add to cart) button. Rebuilt compat-check.min.js.

[2.8.1] - 2026-10-06

Fixed - Security and data bugs from the cross-review of batch A2 + B

  • R-BPB-01: searching within a step (RAM) no longer exposes ECC/Registered RAM or laptop RAM again. WP_Query ignores post__not_in once post__in is present, so the exclusion list is subtracted directly from the search results (TanDoanhBuildPC_Availability::resolve_post_filters(), applied to get_products, the get_products_and_filters fast path, get_step_filters, search_products). The "on sale" filter intersects with the search results instead of overwriting them.
  • R-BPB-02: pagination by "Đánh giá" (Rating), "Giá thấp đến cao/cao đến thấp" (Price low to high / high to low), date and name has a stable secondary key ID, so products are no longer duplicated or lost between pages (availability.php apply_sort()).
  • R-BPB-03: share links are READ-ONLY. A client_session of the form share_* is refused at every endpoint (code share_readonly), every write to a share_* session row is refused at the DB layer (TanDoanhBuildPC_Database::save_session()), and only create_share may create a row. add_product, get_session and save_session share one gate get_session_id() instead of reading the raw client_session. Whoever opens a link still receives a copy in their own session through get_share.
  • R-BPB-04: send-email attempts blocked by the site-wide cap no longer deduct from the customer's IP and recipient quota (the site cap is checked first).
  • R-BPB-06: the message filter additionally blocks bare IPv4 addresses (including ports and paths), every xxx:// scheme (file, ssh, ws, ...), [.], full-width dots and invisible characters.
  • R-BPB-07: check_compat is a public read-only endpoint so it deliberately does not require a nonce (the builder page is cached, and a stale nonce would silently break the Compatibility box); instead it has a limit of 120 requests/minute/IP, including via admin-ajax, and a cap of 40 products per request (previously 60).
  • R-BPB-08: the component picker modal has no more nested scrollbars (only the product column and the sidebar scroll); the search box placeholder changes to "Tìm sản phẩm" (Search products) so the text is not cut off on mobile.
  • The price of the home page preset widget (theme buildband/render.php) is printed in the Vietnamese standard 4.290.000đ.

Changed

  • Search within a step matches both the product name and the SKU (query through $wpdb->prepare, esc_like).

[2.8.0] - 2026-10-06

Added - "Tính trả góp" (Calculate installments) button under the total price

  • Under "Tổng Cộng" (Total) (including the floating total bar on mobile) there is a "Tính trả góp" button that opens the theme's installment modal (window.tdInstallment, tandoanh theme >= 1.9.21) with the amount equal to the current total; when the total changes the amount updates (even while the modal is open); when the total is 0 the button hides (assets/js/buildpc-installment.js, includes/class-frontend.php, td-fixes.css).
  • A summary line of the most recent choice under the total, for example "Trả góp VIB 6 tháng: 8.005.000đ/tháng - Đổi" (Installment VIB 6 months: 8,005,000 VND/month - Change). It is recalculated for the current total and only shows when the choice is still valid under the active fee table, otherwise it says "không áp dụng cho tổng này" (does not apply to this total). The theme's installment JS/CSS is loaded only when the customer clicks the button or already has an earlier choice in the session.

Added - A4 print page rendered on the server (in the style of the ktbh print sheet)

  • Address {Build PC page}?td_print={share key}&builder={id}[&inst=card:VIB:6][&autoprint=1] (includes/print.php, includes/templates/print-sheet.php, assets/css/print-sheet.css, assets/js/print-sheet.js). The configuration comes from the saved share link (only IDs + quantities); name, image, price, stock and warranty are taken from the DB at print time; out-of-stock items carry the label "Tạm hết hàng" (Temporarily out of stock), items priced at 0 say "Liên hệ" (Contact us) and the total states clearly that it does not include that item.
  • Layout: logo and business info (td_biz(): showroom, hotline, web, email), configuration code TD-XXXXXXXX, print date and time, component table, total, the "Kế hoạch trả góp" (Installment plan) block, a QR code that reopens the configuration, and notes. CSS @page A4 with 12mm margins, print-color-adjust: exact, break-inside: avoid for table rows, the table header repeated on every page, page numbers "Trang X / Y" in the bottom margin, the theme's self-hosted fonts (Be Vietnam Pro and Inter, with Vietnamese support). A good preview on screen and phone; noindex (header and meta), no-store, limited to 120 requests/hour/IP.
  • The installment plan is recalculated by the SERVER from the active fee table (tandoanh_installment_resolve() of tandoanh theme 1.9.24, matching installment-core.js): the customer only submits the choice of provider, term and down payment; unusual values, a disabled provider, an unsupported term, or an amount outside the minimum or maximum mean this block is not printed. It includes the monthly payment, a detail table, the payment schedule (first term and later terms if the bank charges a fee in the first term), a term comparison table and the fee table's disclaimer line.
  • The QR is drawn as vector SVG by a standard QR generator (assets/js/vendor/qrcode-generator.js, an MIT core taken from jquery-qrcode bundled with WooCommerce, re-scanned and decoded to the correct link). The old includes/lib/phpqrcode.php did not produce a standard QR so it is not used.
  • The Print and Save PDF buttons both open the print page in a new tab and open the print dialog automatically (the customer chooses "Save as PDF" or a printer); the share link is reused within the session by configuration signature (assets/js/buildpc-print.js). tandoanh_buildpc_create_share accepts no_qr=1 (no call to an external QR service, and it also returns key, with its own quota of 40 requests/hour/IP); the Send Email button uses this mode.

Changed

  • create-pdf.js (html2canvas) is no longer loaded; the files create-pdf.js, print.js, qr-code.js and their .min versions are kept in the assets folder for reference. The Send Email button moved to buildpc-print.js; email.js no longer depends on create-pdf.
  • The configuration email additionally includes a link to the A4 print page (no file attachment).
  • The builder HTML cache key (5-minute transient) includes the plugin version number so changing the template drops the old cache.
  • The .min.js build script: added buildpc-print.js, buildpc-installment.js; removed create-pdf.js, qr-code.js.

[2.7.0] - 2026-10-05

Fixed - The "Thêm Giỏ Hàng" (Add to cart) button always said "Không có sản phẩm nào được chọn" (No product selected)

  • Root cause: add_product/get_session/save_session stored the session by the raw client_session, while add_to_cart, remove_product, update_quantity, clear_all and the dependent queries looked up the session by base|client|hash (and share.php by base|client) so they never saw the saved row. Everything now shares one key: a valid client_session (1-64 characters a-zA-Z0-9_-), and without one the PHP session (get_session_id() in class-ajax.php and share.php). Tested by adding 3 items to a real cart and then emptying it.

Changed - The component picker list shows only items that can be sold (A1)

  • Every builder query, dependent list, filter, search and AI call (includes/availability.php, class TanDoanhBuildPC_Availability): only instock with a price greater than 0. It hides price 0/empty, discontinued, out-of-stock and pre-order items (previously the combined endpoint did not hide pre-orders, and the filter only looked at an option in the settings). add_product refuses items that are no longer on sale. The old hide_* options in the settings no longer have any effect.
  • Search in the builder no longer uses the WP_Query parameter s but filters by name with SQL (every word must be in the name): the livesearch module of tandoanh-code intercepts every product query with s through posts_pre_query, returns site-wide results (ignoring category, stock and price) and causes the Warning "stdClass could not be converted to int".
  • Default sort: best selling (total_sales) descending, then price descending; the key bestseller (the old keys popularity/menu_order also map here). The choices kept: rating, newest, oldest, price low-high, high-low, name A-Z, Z-A. The sort_default setting also changes.
  • Money in the Vietnamese format 1.100.000đ (dot separators, the letter đ) on product cards, the total, AJAX responses, email and PDF (tandoanh_buildpc_money()).

Added - Compatibility and power supply wattage warning

  • includes/compat.php (TanDoanhBuildPC_Compat): the desktop RAM step excludes laptop RAM (SO-DIMM); ECC/Registered RAM shows only when the chosen CPU/motherboard is a workstation platform (Threadripper, Xeon, sTR5, sWRX8, TRX50, WRX80/90, W790, LGA3647); otherwise it is hidden. The cache key follows this state.
  • A yellow (non-blocking) warning when the power supply is smaller than the estimate: (CPU TDP + graphics card TDP + 75W) x 1.4. TDP is taken from the product's tdp, tgp, tbp attribute/meta if present, otherwise from an estimate table by chip family, clearly labelled "Ước tính" (Estimate). The power supply wattage is read from the name (850W) or from the lower bound of a wattage range attribute. A new action tandoanh_buildpc_check_compat, JS assets/js/compat-check.js, a #tdbp-compat frame in the total and a reminder under the PSU step. It also warns when the CPU and motherboard have different sockets or the RAM type does not match the motherboard.
  • Home page preset widget (block tandoanh/buildband, theme file inc/blocks/registry/buildband/render.php calling this plugin): items that violate A1 show "Tạm hết" (Out for now) (or "Liên hệ" (Contact us) when there is no price), the total and pill label become "Liên hệ báo giá" (Contact us for a quote) instead of an undercounted sum, and the "Tương thích" (Compatibility) box really checks (socket, RAM type, power supply) instead of a fixed string.

Changed - Component picker modal

  • A 4-column card grid (main frame above 880px), 3 columns, 2 columns (from 340px, mobile), 1 column when very narrow, via container queries. Form controls (search, sort, native and select2 filters) share one TD brand style, and the mobile filter is a compact 2 columns. Dark mode: fixed the select2 "Thương hiệu" (Brand) with a white background (the original rule used !important), the select2 dropdown, the frame and quantity box of the selected product; an automatic scan found 0 elements below 4.5:1 in light and dark, desktop and 390px (td-fixes.css).
  • Select2 is loaded only once on the builder page (the WooCommerce select2/selectWoo handles become aliases of the plugin's copy).
  • Frontend JS has a .min.js version (terser, the source is kept), used when the .min.js is newer than or equal to the source; turned off with TANDOANH_BUILDPC_USE_MIN=false.

Added - Configuration email

  • An optional "Lời nhắn" (Message) box: plain text up to 300 characters, HTML and every URL/domain/email removed, escaped when inserted into the mail. The modal sends only IDs and quantities; the close button uses SVG, and the success state meets contrast in dark mode.
  • Protection against quota exhaustion: a limit per IP (IPv6 grouped by /64), plus a limit of 3 mails/day per recipient, and a site-wide cap of 100/hour that only counts SUCCESSFULLY sent mail; when the cap is hit a general message is shown ("hệ thống đang gửi nhiều email, hãy dùng Chia sẻ/In/PDF" (the system is sending many emails, please use Share/Print/PDF)) and one warning line per hour is written to the PHP log.

Security

  • Customer IP uses one shared function (includes/ip-helper.php, Cloudflare IPv4/IPv6 ranges hardcoded in cloudflare_ranges()): CF-Connecting-IP/X-Real-IP is trusted only when REMOTE_ADDR belongs to a Cloudflare range or is loopback/an internal network. The fast endpoint buildpc-endpoint.php reuses this function (previously it trusted raw headers).
  • The fast endpoint rate limit rises from 60 to 180 requests/minute/IP (counted by the real IP), with a 429 carrying a Vietnamese message and Retry-After.
  • The 90-day session cleanup cron: runs at 03:20 in the site's timezone (previously it drifted by UTC to 10:20 Vietnam time), deleting in batches of 500 rows; reading a share through the builder, get_share and the home page widget extends updated_at (at most once per 7 days per row). Before the first run it extended every existing row and backed up the table.

Fixed

  • The Warning Undefined variable $phpqrcode each time a share link was created (a dead local QR branch) in share.php.
  • The per-product list/filter cache: the transient key now carries a cache version number (Redis td_buildpc_version); when price/stock changes the old cache is discarded (the old SQL delete pattern did not match the real transient names, and the transients live in Redis so they could not be deleted with LIKE).
  • The total is no longer turned into "Đ" by uppercase CSS; email.js drops &times;; modal-manager.min.js no longer drifts from the source.

[2.6.0] - 2026-10-05

Fixed - Component picker modal UI

  • Mobile: the component picker modal is no longer covered by the site's sticky header (the modal sits inside .entry-content which has z-index:1, so it lost to the header at z=200). When opened, JS lowers the z-index of ancestors that create a stacking context and restores it on close; the modal uses 100dvh + safe-area, and the close button is at least 44x44px (modal-manager.js, td-fixes.css).
  • Dark mode: the modal, select, select2, search box, quantity box, +/- buttons, product cards, pagination and the Send Email modal use colors from the theme's dark tokens (:root[data-theme="dark"]), with contrast from 4.5:1 (td-fixes.css, a new file with no .purged.css version).
  • The "Khách hàng ... đã mua" (Customer ... has bought) toast is hidden when body.modal-open so it does not cover the modal.
  • The modal title and step names are no longer turned by wptexturize from " - " into a long dash: a block theme runs wptexturize() over the whole rendered template so step names are printed with the entity &#45; (helper tandoanh_buildpc_html_name), and JS also normalises en and em dashes to -. Removed the 3px accent bar before the title. Blocked the duplicate print of #tandoanh-buildpc-modal (the Frontend class was initialised twice).
  • Replaced emoji with inline SVG: the "Không tìm thấy sản phẩm" (No products found) icon (a box icon), the Send Email status (check mark / cross icons), and the modal close button.

Fixed - Keyboard / screen reader access

  • #products-modal and the confirmation modal have role="dialog", aria-modal, aria-labelledby; the close button, the share/email/print/PDF buttons, the component edit/delete buttons (with the product name), the +/- buttons and the quantity box have aria-label; filter labels are tied with for/id, and filter boxes have name.
  • The Esc key closes the modal (not when Select2 has a dropdown open), Tab is kept inside the modal, focus moves into the modal on open and returns to the calling button on close. The Send Email modal also returns focus.

Security - Sending the configuration by email (tandoanh_buildpc_send_email)

  • The email content is built by the server from product IDs + quantities (name and price taken from the DB, escaped). HTML, subject, content and PDF files sent by the client are dropped entirely; the modal no longer has the Subject/Content/Preview boxes.
  • Rate limiting per IP: at most 3 emails/hour and 10 emails/day, plus a site-wide cap of 100 emails/hour (filter tandoanh_buildpc_email_global_per_hour). Only 1 address is accepted per request (multiple addresses, the Name <mail> form and separators are refused), is_email, and a hidden honeypot tbpc_website.
  • The share link in the email is used only when it points to the site itself and the share key exists in the DB.
  • The customer IP (TanDoanhBuildPC_Security::client_ip()) trusts CF-Connecting-IP/X-Real-IP only when REMOTE_ADDR is a trusted proxy (loopback, internal network, Cloudflare ranges, or the filter tandoanh_buildpc_trusted_proxies).
  • create_share for anonymous visitors: limited to 20 requests/hour/IP. Added a daily 03:20 cron tandoanh_buildpc_cleanup_sessions that deletes customer sessions (user_id = 0) not updated for more than 90 days, at most 10,000 rows each time (filter tandoanh_buildpc_session_retention_days, minimum 30). A share link read again after its transient has expired is extended (updated_at).
  • Moved the audit report outside the webroot and deleted assets/js/debug-submit.js (no longer loaded anywhere).

Fixed - The Redis response cache did not refresh when price/stock changed

  • The fast endpoint buildpc-endpoint.php read the Redis key td_buildpc_version but nothing used to increment this key. It now bumps it (INCR, debounced 2 seconds/request, compensated at shutdown) on woocommerce_update_product, woocommerce_update_product_variation, woocommerce_product_set_stock, woocommerce_variation_set_stock, woocommerce_product_set_stock_status, woocommerce_variation_set_stock_status, woocommerce_delete_product, save_post_product. The transient delete query runs only once per request.

Fixed - Assets without a version

  • Every frontend JS/CSS is enqueued with version = filemtime, plus the filter tandoanh_buildpc_readd_asset_ver (priority 10001) that re-attaches ?ver= after wp-extra strips it.

[2.5.1] - 2026-08-23

Added - Self-hosted auto-update through phatdev.net

  • Added a dedicated updater (includes/plugin-updater.php, class TD_Buildpc_Updater) that checks https://phatdev.net/api/updates/tandoanh-buildpc and shows the new version on the WordPress Updates screen, downloading the package from the update server with a license. It needs define('TD_UPDATE_LICENSE', '...') in wp-config.php; without a key it stays silent (no-op). The ZIP package is served only to a valid active license; the server logs and rate-limits per IP; the manifest is cached for 6h and cleared after an update.

[2.5.0] - 2026-07-09

Feature - P3: warnings for component price/stock drift + manual price update

Completes Phase 3 of the Build PC admin plan. Principle: never change a price silently - the system only reports and suggests, and the user must press the button before anything is written.

  • build_diff( $pid ) (new helper): compares _td_build_parts with the CURRENT state of each component and classifies 3 levels - missing (product deleted/trashed), oos (out of stock), price (price differs by 1 VND or more from _td_build_snapshot). It returns the current component total, the suggested price (= total + the saved assembly fee), the current selling price, and the flag was_override (price at apply time != total + fee, meaning sales typed the price by hand).
  • Warning panel in the metabox: a table listing ONLY the components with problems (price at apply time / current price / difference in color, up red and down green) + count chips per type. A summary line "current selling price → price based on current components", stating the component total + assembly fee. If the build was ever manually overridden, a warning that pressing the button will overwrite that price. When everything matches: the line "Giá và tồn kho các linh kiện vẫn khớp" (Component prices and stock still match), with a check mark.
  • The "Cập nhật giá theo linh kiện" (Update price from components) button → AJAX td_buildpc_refresh_price (nonce per product + edit_post capability): sets the regular price = current component total + the saved assembly fee, clears the sale price, and rewrites _td_build_snapshot as the baseline for the next comparison. It shows a confirm() before running.
    • Safety lock: if any component was deleted (n_missing > 0) the button is NOT shown, and the message only says "mở Build PC chọn lại" (open Build PC and choose again) - to avoid a wrong price from a missing item.
  • A "Build PC" column in the product list (inserted right after the Price column): a badge N linh kiện mất (N components missing) (red) / N hết hàng (N out of stock) (yellow) / N đổi giá (N price changes) (blue, with "đề xuất ±X đ" (suggested ±X VND)), or a "khớp" (match) badge. Products without build meta exit immediately, so there is no query cost for thousands of ordinary products.

QA round 2 - 4 bugs caught and fixed before handover

  • [Performance, the heaviest] The list column cost 76 queries / 108ms for EACH bundled PC (20 bundled PCs per page means about 1.5k queries, about 2 seconds) because build_diff() built a full WC_Product for each component. Added build_flags(), a light version that reads _price + _stock_status directly with 2 SQL statements (ids already absint, safe to interpolate) → 5 queries / 8.5ms (93% lower). Verified that build_flags() gives results IDENTICAL to build_diff() (n_missing/n_oos/n_price/suggested).
  • [Correctness] build_flags() swallowed items when the configuration had duplicate ids: it assigned $qty_by_id[$id] = qty (overwriting) instead of accumulating, so 2 rows of the same component (2 RAM sticks) counted only 1, and the suggested price column was short by 5,690,000 VND while the metabox calculated correctly. Fix: accumulate qty. (sanitize_parts() dedupes so the AJAX path does not produce duplicates, but the meta can be written by import/sync/manual edits - the column shows MONEY so it must defend itself.)
  • [UI rule] Money fields used type=number (assembly fee, final selling price) - which violates the td UI rule: a spinner + mouse wheel changes the value + no dot grouping. Changed to type=text + inputmode=numeric + class td-money, JS formats 1.234.567 while typing and strips back to digits on submit. Server-side lock: ajax_apply_build() strips every non-digit character before casting - previously absint("300.000") = 300, so an assembly fee of 300,000 VND would have become 300 VND if JS had not stripped it.
  • [Robustness] render_build_column() read keys from the array ['has_build'=>false] when the _td_build_parts meta was corrupt (a string instead of an array) → a PHP warning. It now uses build_flags(), which returns null → prints a dash.
  • Added _prime_post_caches() in build_diff() (metabox).

QA round 2 (continued) - NEW warning: components without a price

A screenshot of the real metabox exposed a money risk that the original P3 did NOT catch: sample product 104670 has 3/8 components priced at 0 VND (SSD + AIO still on sale, PSU out of stock), so the component total was missing the money for 3 items and the computed selling price came out short with no warning.

  • Added the flag n_zero (price <= 0 but the product still exists) to both build_diff() and build_flags(); rows also have a zero key.
  • Panel: a red chip N chưa có giá (N without a price) and a red warning block "tổng linh kiện đang thiếu tiền các món đó, giá bán tính ra sẽ hụt - nhập giá linh kiện trước, hoặc tự gõ Giá bán cuối" (the component total is missing the money for those items and the computed selling price will fall short - enter component prices first, or type the Final selling price yourself); each component line with price 0 has a chưa có giá chip.
  • List column: a red badge N chưa có giá (placed before the out-of-stock badge).
  • Update price button: the confirm() becomes an uppercase warning that states the number of items without a price before allowing the price to be set.

QA round 2 (continued) - 3 minor points from the security/performance review

  • current_price drifted between the 2 functions: build_diff read get_regular_price(), build_flags read _price (the effective price). When the bundled PC is on sale, the list column and the metabox reported different differences. Fix: build_flags reads _regular_price - verified by setting a sale of 40 million, and both functions return 43,760,000.
  • _price with several rows (old WC variable products): the SQL took the last row (possibly the max price) while get_price() returns the min → a mismatch. Fix: keep min() when a duplicate _price key is met.
  • ajax_refresh_price did not block product types: set_regular_price() on variable/grouped products has no effect (the price lives in the variations), so it reported success while the price did not change. Fix: only allow is_type('simple').

Security audit (separate agent): no critical/serious findings. Nonce + capability are in place on all 3 AJAX handlers (nowhere is a UI button merely hidden while the server skips the check); the SQL in build_flags() is safe (ids cast with (int) + absint, $in contains only digits); no XSS (the column only echoes cast numbers, and product names always go through esc_html). Regression: 10/10 edge cases PASS (duplicate ids, qty>1, corrupt meta, empty snapshot, ordinary products, n_zero/n_oos/current_price matching between the 2 functions), and onbackorder is consistent (both functions do NOT treat it as out of stock).

UI QA (Playwright, real admin): all PASS. The money field is type=text + inputmode=numeric, 40px high, typing 43760000 shows 43.760.000, the mouse wheel does not change the value; the panel does not overflow horizontally at 1440 and 1280; the "Build PC" column sits right after the Price column; ordinary products show a dash; 0 console errors, 0 4xx/5xx requests.


[2.4.0] - 2026-07-08

Feature - Create/edit PC products (bundled PCs) with the Build PC interface right inside admin

  • New file includes/admin-product-builder.php (class Tandoanh_BuildPC_Admin_Product):
    • The "Cấu hình Build PC" (Build PC configuration) metabox on the product edit screen for the Tân Doanh bundled-PC category tree (id 22) or a product that already has build meta. The "Mở Build PC" (Open Build PC) button opens an iframe modal of the frontend builder page (?td_admin_build=<pid>&tdab=<nonce>, with ?td_parts= preloaded when re-editing an old configuration) - sales staff choose components exactly like customers on the website, using the existing server-side session.
    • An admin-mode bar at the bottom of the builder page (shown only with a valid nonce + a user with edit_post): the button "Dùng cấu hình này cho sản phẩm" (Use this configuration for the product) reads the session's selected_products through AJAX td_buildpc_session_parts and then postMessages the parts list back to the metabox (same-origin).
    • AJAX td_buildpc_apply_build (nonce per product + edit_post capability): writes the meta _td_build_parts (source of truth) + _td_build_snapshot (each item's price at apply time, the base for later price/stock drift warnings); selling price = component total + assembly fee (option tandoanh_buildpc_assembly_fee, remembering the last value) or the final price override box; the featured image is taken automatically from the product in the CASE step; the configuration table is written into the description between the markers TD-BUILD:START/END (re-applying does not duplicate it); the TD standard bundled-PC short description (only when it is empty); a name suggestion "PC TD (CPU, Main, VGA, RAM, SSD)" using the tandoanh-benchmarks extractor for short chip names (only when the name is empty/the default draft). The product keeps its status - it never publishes automatically.
    • A 1200x1200 SVG infographic around the case: the case image embedded as a data-URI in the middle, gold/ink callouts on both sides (left CPU/Main/RAM, right VGA/SSD/PSU, bottom cooler/HDD/LCD) + a product name header + a brand footer; saved as an attachment (the SVG mime is opened, scoped, for exactly 1 upload), automatically replacing the old one, and inserted at the start of the gallery (image at position 2). The re-generate button = tick "Tạo infographic" (Create infographic) and apply again.
    • Menu Build PC → "Tạo sản phẩm PC" (Create PC product): creates a draft product with the bundled-PC category pre-assigned, then goes straight to the edit screen that has the metabox.
  • Verified end to end on test product 104670: 8 real components → price 43,460,000 + fee 300,000 = 43,760,000, case image, an 8-row table, excerpt, name "PC TD (Ryzen 7 7700, Gigabyte X870E AORUS MASTER, RX 9060 XT, …)", and the infographic rendered with the right layout (fixed the left column text overflow: every callout is left-aligned, values cut at 34 characters).

Fixes from the QA agent (same day)

  • Fatal 500 on the get_share endpoint / configuration restore (includes/class-frontend.php calculate_total()): a product WITH NO PRICE → get_price() returns an empty string → PHP 8 "Unsupported operand types: int * string" → the builder could not reload a saved configuration (every step empty, total 0 VND). Fix: cast (float) get_price() * (int) $quantity; verified with the exact faulty data (2 products with empty prices) → calculates 16,890,000 with no fatal.
  • admin-builders-list.php: PHP Warning Undefined property $created_at → a !empty() guard + shows "-" when missing.
  • The "Chỉnh sửa/Xoá" (Edit/Delete) buttons in the builder list card footer were 24px high → min-height 36px (admin-builders.css).

[2.3.0] - 2026-07-02

Feature - Preload components into the builder via the URL ?td_parts=

  • New file includes/preload.php: a template_redirect hook that receives ?td_parts=1,2,3 (at most 15 ids, absint, published products only), maps each product to the builder step by the step's categories (also adding get_ancestors because products are only assigned to leaf categories), stores selected_products in a 1-hour share transient, then redirects to ?tandoanh_share=<key>&builder=1 - the existing share flow of SessionManager loads it, renders it and persists it into the customer's session. No new JS needed.
  • Used by the buildband block on the tandoanh theme home page: the "ĐỔI" (CHANGE) and "Tuỳ chỉnh cấu hình" (Customise configuration) buttons take the customer to the builder page with the whole preset set of components already selected.

[2.2.5] - 2026-05-18

Fix - "Plugin không có đầu trang hợp lệ" (The plugin does not have a valid header) when clicking Activate after updating via zip

Symptom: After uploading a new zip file through Admin → Plugins → Add New → Upload Plugin, WP shows a success page with the "Kích hoạt plugin" (Activate plugin) button. Clicking it leads to an error page:

Plugin không có đầu trang hợp lệ. (The plugin does not have a valid header.)

When leaving to /wp-admin/, the plugin is already active, so users get confused and cannot tell whether activation succeeded or failed.

Root cause: The MU plugin template file runtime/tandoanh-fast-buildpc.php has the header Plugin Name: TanDoanh Fast BuildPC Bootstrap. When WP Plugin_Upgrader::plugin_info() calls get_plugins('/tandoanh-buildpc') to find the main plugin file in order to build the Activate button URL, WP scans 1 child level of the plugin folder recursively and parses every .php file with a Plugin Name: header → it catches 2 files:

  • tandoanh-buildpc.php (the main plugin)
  • runtime/tandoanh-fast-buildpc.php (the MU template, not the main plugin)

WP takes array_keys($plugins)[0] - depending on the readdir order of the filesystem it may return file 2 → the Activate button points to the URL plugin=tandoanh-buildpc/runtime/tandoanh-fast-buildpc.php. When clicked, validate_plugin() calls the global get_plugins() (no folder filter) - that function scans only 1 level deep, does NOT find the 2-level file → WP_Error('no_plugin_header') = "Plugin không có đầu trang hợp lệ".

Fix: Removed the Plugin Name: header from the source file runtime/tandoanh-fast-buildpc.php. Replaced it with a comment explaining the MU template role + a warning not to add the header back. The function tandoanh_buildpc_sync_mu_plugin() still copies the file to wp-content/mu-plugins/tandoanh-fast-buildpc.php as before - an MU plugin runs without Plugin Name:, and the WP admin "Must-Use Plugins" page uses the filename as the name.

Verify: after the fix, get_plugins('/tandoanh-buildpc') returns only 1 entry tandoanh-buildpc.php → the Activate button points to the correct path → activation succeeds with no error.


[2.2.4] - 2026-05-15 (Admin Menu & Settings English Labels)

Language Policy - Consistent with SL-Migrations

Rule: Short labels / menu items / tabs / button text → English; Vietnamese is kept only for long descriptions, tooltips and error messages.

Label changes

Sidebar submenu (Build PC):

  • "Thêm mới" → "Add New"
  • "Cài đặt" → "Settings"
  • "Đồng bộ AI" → "AI Sync"
  • Page title Đồng bộ AI → AI Sync

Builders list page (admin-builders-list.php):

  • "Thêm Builder Mới" → "Add New Builder"
  • "Chưa có Builder nào" → "No builders yet"
  • "Tạo Builder Đầu Tiên" → "Create First Builder"
  • The description stays Vietnamese ("Quản lý các cấu hình build PC của bạn." (Manage your PC build configurations.))

Builder edit page (admin-builder-edit.php):

  • "Chỉnh sửa Builder" → "Edit Builder"
  • "Tạo Builder Mới" → "Add New Builder"
  • "Cập nhật Builder" → "Update Builder"
  • "Tạo Builder" → "Create Builder"

Settings page (admin-settings.php):

  • Page heading "Cài đặt Build PC" → "Build PC Settings"
  • Tab "Chung" → "General"
  • Tab "Hiển thị" → "Display"
  • Tab "Sản phẩm" → "Products"
  • Tab "Nâng cao" → "Advanced"
  • The AI Assistant / Backup / Debug tabs stay English (already were)

Notes

  • Long description text inside cards (helper text, instructions) stays Vietnamese because it is explanatory guidance for users.
  • Notice messages (success / error toast) stay Vietnamese.
  • Detailed form field labels inside each tab may still be Vietnamese - to be reviewed in a later version if needed.

[2.2.3] - 2026-05-15 (Fix Info Tab Changelog Parser)

Bug Fix - The "Thông tin" (Info) tab showed the wrong version + changelog

Symptom: The "Thông tin" tab in the Build PC admin showed "Version 2.1.0" and the changelog of v2.1.0 (Fast Endpoint All Actions · Security Fixes · UI Redesign), even though the plugin had been updated to 2.2.2 since 2026-05-12.

Cause: The function get_latest_changelog() in includes/class-admin.php used a regex to parse version headings in CHANGELOG.md with a title in () that was mandatory:

preg_match('/^##\s*\[([^\]]+)\]\s*-\s*([0-9]{4}-[0-9]{2}-[0-9]{2})\s*\(([^)]+)\)/', $line, ...)
  • ## [2.2.2] - 2026-05-12 → NO title → the regex skips it
  • ## [2.2.0] - 2026-04-18 → NO title → the regex skips it
  • ## [2.1.0] - 2026-04-18 (Fast Endpoint All Actions...) → has a title → matches → the parser takes content from this entry

→ The "Thông tin" tab showed 2.1.0 instead of 2.2.2.

Solution:

  • Change the title in the regex from mandatory to optional: (?:\s*\(([^)]+)\))?
  • Update the render template: print the " - " + title only when the title is not empty (avoiding a stray dash in "Version 2.2.2 - ")

Files changed:

  • includes/class-admin.php:1086 - the regex parser
  • includes/class-admin.php:1217 - the version header display template

Benefit: Every CHANGELOG.md entry can be parsed, including entries without a title. This ensures the "Thông tin" tab always shows the latest version correctly.


[2.2.2] - 2026-05-12

Bug Fix - Fixes "Security verification failed" caused by WP Rocket caching HTML with an expired nonce

Symptom: When customers click "Thêm vào cấu hình" (Add to configuration), "Lưu cấu hình" (Save configuration), "Gửi email" (Send email)..., they sometimes get the error "Security verification failed". Clearing the cache fixes it temporarily, and a few hours later it happens again.

Root cause:

  • wp_localize_script embeds wp_create_nonce('tandoanh_buildpc_nonce') directly into the HTML
  • WP Rocket caches the HTML → every customer within the same cache lifespan uses the SAME nonce
  • A WordPress nonce lives 12-24h by default (nonce_life = 86400s, split into 2 ticks of 12h)
  • When the cache file lives for more than 12h, customers hit a nonce that is about to expire or has expired → verify_nonce_strict() rejects 7 endpoints: add_product, remove_product, update_quantity, send_email, clear_all, add_to_cart, save_session

Solution - the class TanDoanhBuildPC_NonceRefresh (includes/class-nonce-refresh.php):

  • The AJAX endpoint td_buildpc_refresh_nonce (both wp_ajax_ + wp_ajax_nopriv_) returns a fresh wp_create_nonce with nocache_headers(). admin-ajax.php is not cached by WP Rocket, so the nonce is always fresh.
  • Inject inline JS after tandoanh-buildpc-app:
    • On DOM ready: call the refresh endpoint and overwrite window.tandoanh_buildpc_ajax.nonce
    • setInterval every 30 minutes: keep refreshing for users who leave a tab open for a long time
    • global: false: does not trigger the plugin's shared error handler

Graceful architecture:

  • If the refresh fails (network/timeout) → JS uses the old nonce in the HTML → the behaviour is the same as before, no worse
  • The original plugin's existing AJAX logic is not modified - only a new class + 1 require + 1 instantiate in tandoanh-buildpc.php are added

Reusable pattern: It applies to any plugin that has the same problem with WP Rocket / page-level cache + nonce. The original plugin already had this pattern for admin (class-admin.php + admin.js), and the gap filled here is the frontend.


[2.2.0] - 2026-04-18

Admin UI alignment

The Build PC admin UI was aligned with the Tân Doanh Code plugin - the same navy blue palette, with no more mixed purple/orange/green.

What changed:

  • The status badge "Đang chạy" (Running) in a builder → blue (instead of green)
  • The status badge "Tạm dừng" (Paused) in a builder → grey (instead of orange-yellow)
  • Modal title → navy blue gradient (instead of purple)
  • The select2 dropdown on hover → blue (instead of light green)
  • The tick icon in the admin page → blue (instead of green)
  • Builder page width → 1400px
  • The version number is used consistently for CSS/JS to avoid stale cache errors

[2.1.0] - 2026-04-18 (Fast Endpoint All Actions · Security Fixes · UI Redesign)

Performance

  • Fast endpoint for all session/product actions: add_product, remove_product, update_quantity, save_session, get_session, get_share, clear_all now all go through buildpc-endpoint.php instead of admin-ajax.php - reducing from about 1.5s to about 250ms per action
  • Removed the redundant loadSession() after add_product: Previously every product addition made one extra unnecessary session reload request; now it uses the data directly from the add_product response

Security

  • Redis caches only successful responses: Previously it cached errors too - now it checks "success":true before saving to Redis
  • Removed X-Forwarded-For from the IP rate-limit chain: This header can be forged by clients; only CF-Connecting-IP (Cloudflare) and X-Real-IP (nginx) are trusted
  • Share key entropy: Changed from md5(rand()) (weak) to bin2hex(random_bytes(8)) - 64-bit CSPRNG
  • Removed the $_GET['nonce'] fallback in save_client_logs(): Only accepts the nonce through $_POST
  • Redact the nonce from the debug log: nonce, security, _wpnonce are filtered out before logging

UI Redesign

  • td-theme.css v2: A new look following the tandoanh.vn brand - navy #015c9e + red #dd3333 + bg #f0f2f5
    • Header: flat navy, white title, red underline accent
    • Step cards: white bg, navy left border, hover → red
    • .step-title: 16px bold with !important
    • Modal header: flat navy, title 1.2em bold
    • Summary sidebar: white card, red inline price 28px
    • The "Xóa Tất Cả" (Clear All) button: ghost style (transparent → red on hover)
    • Share icon buttons: gray bg, hover → navy
  • Removed the inline yellow style (style="background:#ffd700") on the "Xóa Tất Cả" button in the PHP template

Technical

  • JS versions: session-manager 1.0→1.1, product-manager 1.0→1.1, modal-manager 1.1→1.2
  • td-theme.css is enqueued last in the CSS chain (depends: modular + simple-icons) to override correctly

[2.0.5] - 2026-03-04 (Filter Pagination Bug Fix)

Bug Fixes

  • Filter state lost during pagination: Fixed critical bug where attribute filters (chipset, socket, etc.) were not preserved when navigating to page 2+
    • Before: Choose chipset Z790 → Page 1 correct, Page 2 shows both B760M and Z790
    • After: Choose chipset Z790 → All pages show only Z790 mainboards
  • Root cause: ModalManager.loadProducts() did not save filters into the currentFilters state
  • ModalManager state management: Filters are now persisted across pagination, search and sort operations

Improvements

  • Consistent filtering: Filter state is maintained throughout all user interactions
  • Better UX: Users are no longer confused when a filter "disappears" on page 2+
  • Centralized state: Consolidate the filter state in ModalManager.currentFilters

Technical Changes

  • Modified file: assets/js/modules/modal-manager.js
  • Fix location: Line ~215 in the loadProducts() method
  • Change: Added this.currentFilters = filters to persist state before the AJAX call
loadProducts: function(stepId, page = 1, search = '', filters = {}, ...) {
    // FIX: Update currentFilters so pagination preserves filter state
    if (filters && typeof filters === 'object') {
        this.currentFilters = filters;
    }
    ...
}

[2.0.4] - 2026-03-02 (PDF Warranty Column Fix)

Bug Fixes

  • Fix warranty parsing logic: The regex in parseWarrantyString() now requires a unit (tháng/năm, i.e. month/year)
    • Before: Matched any number → "Mới 100%" (100% new) became "100 Tháng" (100 months)
    • After: Matches only when there is a unit → "12 tháng" = "12 Tháng", "2 năm" = "24 Tháng"
  • Case warranty override: Products in the "Case" step always show "N/A" in the warranty column in the PDF/print
  • Default warranty to N/A: Every product for which no warranty information is found shows "N/A" instead of being left blank
  • Script versioning: Moved create-pdf.js and qr-code.js from a hardcoded '1.0.0' to TANDOANH_BUILDPC_VERSION so the cache is invalidated automatically on update

Improvements

  • Consistent warranty parsing: The JavaScript logic is now consistent with the PHP backend (both require a unit)
  • Better UX: The warranty column always has a clear value ("N/A" or "X Tháng"), with no more blank cells causing confusion
  • Cache invalidation: The browser cache clears automatically when the plugin version is bumped

[2.0.3] - 2025-11-12 (Mobile UX Enhancement)

Design (Mobile)

  • Fix modal header overlap: Solves the problem of the modal header being hidden by the site header on mobile
  • Dynamic padding: JavaScript automatically adjusts the modal's padding-top based on the position of the site header
  • Scroll detection: When the site header scrolls out of view → the padding automatically returns to 0, removing the extra blank space
  • Improved close button: Increased the tap target size to 44x44px (WCAG), added a background and border-radius
  • Compact filters: Tightened the filter spacing on mobile (padding 5px, gap 8px, width 160px)
  • Hidden filter title: Hides the text "Bộ lọc" (Filters) to save space
  • Smaller clear button: Reduced the padding and font-size of the "Xóa bộ lọc" (Clear filters) button
  • Gradient modal title: Added a gradient text effect (blue→purple→pink) with a 3px border accent

Optimisation

  • Load minified CSS: Switched from .css to .min.css for mobile and modular styles
  • CSS size reduction: mobile.css 9.7K → 4.9K (50%), modular.css 31K → 23K (26%)
  • Passive scroll listener: Optimised the scroll event with the { passive: true } flag
  • Memory cleanup: Automatically removes the scroll listener when the modal closes

Customisation

  • Modal z-index hierarchy: Modal 9999 > Modal-header 1060 > Site header 1001
  • Sticky header: The modal header is position sticky with a smooth 0.3s transition
  • Flexbox modal-content: height 100vh, flex-direction column for a responsive layout

[2.0.2] - 2025-11-02 (Security Hardening & Performance Optimization)

Added

  • New AI: Gemini Flash 2.0: Updated and switched the AI from 1.5 Flash to 2.0 Flash

Critical Bug Fixes

  • Fix AI Module Not Loading: A variable typo $ai_init → $ai_init_file kept the AI from loading
  • The chat widget shows again: The AI Assistant now works normally on the frontend
  • Fixed a typo: The variable was declared as $ai_init_file but the check used $ai_init (missing _file)

Security Hardening (8 serious vulnerabilities)

  • Fix #1 - Log File DOS Attack: Limited to 10MB with auto-rotation, deleting backups older than 7 days, truncating messages over 10KB
  • Fix #2 - SQL Injection: Whitelist columns, validate with the regex /^[a-z_]+$/, escape with esc_sql()
  • Fix #3 - Session Hijacking: Validate the format [a-zA-Z0-9_-]{1,64}, a SHA256 hash with the IP, expiration 1h
  • Fix #4 - Email Header Injection: Strip carriage-return and line-feed characters and %0a%0d, a domain whitelist, validate URLs with filter_var()
  • Fix #5 - AI Rate Limiting: A 3-layer system (10/min per IP, 20/min per User, 50/hour per UA), returns retry_after
  • Fix #6 - Share Nonce: Changed from soft to strict verification for write operations, logging security events
  • Fix #7 - PDF Upload: Limited to 5MB, validate MIME with finfo, sanitize the filename, permissions 0600
  • Fix #8 - XSS Prevention: Only allow data:image/png|jpeg, strip unsafe data URLs

Performance Optimization

  • Automatic Cache Invalidation: Automatically clears the cache on product update/delete/stock change
  • 4 WooCommerce Hooks: woocommerce_update_product, delete_product, set_stock_status, variation_set_stock_status
  • Composite Database Index: Added idx_session_builder (session_id, builder_id) for faster queries
  • Auto-detect Index: Automatically creates the composite index on upgrade if it is missing
  • Cache Invalidation Logging: Logs the number of cache entries cleared in debug mode

Security & Performance Audit

  • Audit report: a summary of security 9.5/10 and performance 9.0/10
  • 100% AJAX Protection: 58/58 endpoints have nonce verification
  • Output Escaping: 111 instances of esc_html/esc_attr/esc_url/wp_kses
  • Query Optimization: 96% fewer queries (51 → 2) with WP_Query cache warming
  • 2 Minor Findings: Assessed as LOW risk, no need to fix immediately
  • Docs cleanup: deleted 3 redundant report files, keeping a single audit report

Technical Changes

  • Modified Files: 5 core files updated
  • tandoanh-buildpc.php: Log rotation + AI module fix
  • includes/class-ajax.php: Security fixes + cache invalidation hooks
  • includes/class-database.php: SQL injection prevention + composite index
  • includes/share.php: Strict nonce verification
  • includes/ai/class-ai-ajax-handler.php: 3-layer rate limiting

[2.0.1] - 2025-10-30 (Backup & Settings Enhancement)

Added

  • Complete Debug tab: Added a Server Health Monitor with memory usage, database size and cache status
  • AI Log Management: Buttons to delete and download AI logs with full security checks
  • System Information: Shows the plugin version and the WordPress, PHP and WooCommerce versions
  • Debug mode toggle: Turn debug logging for AI activities on/off

Improved Backup & Restore

  • Full backup: Exports all plugin settings (general, AI, debug, metadata)
  • Security fix: Synchronised the nonce tandoanh_buildpc_nonce between frontend and backend
  • Import logic: Added debug settings and metadata to the import process
  • UI improvements: More detailed descriptions for the export/import functionality

Fixes

  • Nonce mismatch: Fixed the "Security check failed" error when backing up/restoring
  • Missing settings: Added debug_enabled and last_indexed_count to the backup
  • Import validation: Improved error handling and feedback messages

UI Improvements

  • Server Health Grid: A 2-column layout with progress bars for memory usage
  • Debug cards: Separated Debug Settings and Log Management into their own cards
  • Responsive design: Optimised how the debug tab displays on mobile devices

[2.0.0] - 2025-10-28 (AI Index - Optimised for the AI Assistant)

AI Index - Optimising data for the AI

  • Smart product filtering: From 4522 → 1544 products (filters out price <= 0, unsuitable stock, out-of-stock names)
  • The AI receives the right list: The index contains only products suitable for building PCs, no junk data
  • Lower AI output tokens: A leaner index → saves 35-45% of Gemini API cost
  • Rebuild state saving: Automatically saves the number of indexed products to track progress

UI & Features

  • Redesigned Settings Dashboard: A clean new interface that makes the AI Index easier to manage
  • Dashboard sync: Shows "1544 sản phẩm được index" (1544 products indexed) in real time
  • Custom Rebuild scheduling: Supports hourly, daily and weekly automatic rebuilds
  • Auto-update info: Cache info updates automatically when a rebuild finishes

Technical Optimisation

  • New AJAX handler: tandoanh_buildpc_get_last_indexed_count gets the state from options
  • Centralised JS script: Moved all rebuild logic to admin-settings.js
  • Removed duplicate code: Removed the old handler that overwrote it from the assets
  • Optimised database queries: 1 query per batch instead of multiple queries

Fixes

  • Fix product display: Uses an accurate COUNT(DISTINCT) instead of COUNT(*)
  • Fix index calculation: Based on the actual indexed products (1544), not the scan offset
  • Fix 2 conflicting scripts: Removed the old click handler to avoid overwriting logic
  • Hide the GPU count: Removed the unneeded GPU information, replaced by the total number of indexed products

[1.6.8] - 2025-01-17 (CSS/JS Performance Optimisation & Info Tab)

Performance optimisation (IMPORTANT)

  • 850KB less page weight: CSS and JS load only when the [tandoanh_buildpc] shortcode is present
  • 20 fewer HTTP requests: Nothing loads any more on the home page, product pages or blog
  • PageSpeed +10-15 points: Optimised for SEO and user experience
  • Fix AI admin error: Fixed a null get_post() that caused a fatal error

Added

  • Info tab in Admin: Shows plugin information, statistics and the latest changelog
  • Consistent colors: The admin UI uses purple, like tandoanh-code
  • Accordion changelog: View the update history directly in admin
  • Quick statistics: Shows the number of builders, steps and the active status

Changed

  • Smart CSS/JS enqueue: Checks for the shortcode before loading assets
  • Optimised AI module: Loads the AI chat only when the shortcode is present
  • Improved documentation: CSS-JS-OPTIMIZATION.md with detailed guidance

Fixes

  • Fix null $post error: Fixed the error in ai/init.php when get_post() returns null
  • Fix asset loading: No more loading 20 files on every page
  • Fix duplicate check: Removed the duplicated shortcode check logic

[1.6.7] - 2025-10-16 (Backup/Restore & Multi-Builder)

Added

  • Backup/Restore Builder: Export/import a builder configuration as JSON
  • Full backup: Backs up builder + steps + plugin settings in 1 file
  • Multi-Builder AI: A dropdown to choose a builder when there is more than 1 builder
  • Current builder notice: Shows the name of the builder being worked on in the AI admin

Changed

  • Backup/Restore interface: A separate tab in the admin settings with drag-drop import
  • Improved AI admin: Automatically shows the dropdown when there are multiple builders
  • Advanced copy shortcode: Better error handling with the Clipboard API

Fixes

  • AI admin CSS: Fixed CSS not loading in the AI admin
  • Duplicate menu: Removed the duplicated AI admin menu
  • Access permission error: Fixed the "not allowed to access" error when switching builders
  • Copy shortcode: Fixed a false-positive error message

[1.6.6] - 2025-10-15 (AI upgrade & same-brand components)

Improved AI advisory (IMPORTANT)

  • AI fix: The AI now works with low budgets and no longer skips customers with small budgets
  • Smarter advice: The AI automatically chooses the upgrade approach that suits each budget level
  • New component selection order: Prioritises Main→memory→storage→Power supply→Cooler→case→Graphics card for balance
  • Same-brand components: The AI chooses 4 components from the same maker (Main + Power supply + Liquid cooler + case)
  • Prefer new graphics cards: The AI chooses newer RTX cards instead of old ones
  • Automatic adjustment: The AI fixes itself when the budget is exceeded

Same-brand components (NEW)

  • 4 matched components: Main + Power supply + Liquid cooler + case from the same maker
  • 5 major brands: ASUS, MSI, Gigabyte, Corsair, NZXT
  • Synchronised LED lighting: Uses the same lighting control software
  • Better looks: Matching design and colors
  • Better warranty: Only one maker to contact

More accurate AI advice

  • Balanced budget split: Fixed storage that was too expensive and cooler and main that were too cheap
  • Smart upgrade mode: Upgrades a lot when the budget is low and little when the budget is almost used up
  • Correct priorities: Main and memory matter more than the graphics card
  • Consistency: The suggestion AI and the improvement AI use the same selection method

NVIDIA graphics card selection

  • Prefer newer generations: Chooses the new RTX 50 series instead of the old RTX 40 series
  • Not conservative: Chooses a powerful card when the budget allows
  • Priority order: RTX 4090 > RTX 4080 SUPER > RTX 4070 Ti
  • Fits the budget: Chooses the best card within the price range

Search fixes (IMPORTANT)

  • Smoother search: Search right after filtering without hanging
  • Flexible operation: You can search, filter and sort continuously without being blocked
  • Fast response: Cancels the old operation when a new one starts
  • No more getting stuck: Removed the checks that froze the interface

Easier to use

  • Smooth workflow: Filter → search → sort continuously with no lag
  • Instant response: Every action gives a result immediately
  • No freezing: The interface no longer "locks up" after filtering

Technical improvements

  • Better management: Optimised how AJAX calls are handled
  • State synchronisation: Improved loading state management
  • Performance optimisation: Reduced unnecessary AJAX blocking

[1.6.4] - 2025-10-15 (Component compatibility fix)

Socket compatibility fix (IMPORTANT)

  • Accurate checking: An Intel processor is compatible only with a main board of the right socket, with no mix-ups
  • Strict blocking: LGA 1151, 1200, 1700 and 1851 cannot be installed together
  • Smarter AI: The AI knows which socket is compatible with which socket
  • Pre-check: Blocks wrong combos before suggesting

AI improvements

  • Accurate suggestions: The AI does not suggest a processor and main board that are incompatible
  • Better message passing: Fixed user messages getting lost
  • Brand check: Better checking of the graphics card brand

Fixes

  • Block wrong sockets: Does not allow LGA 1700 and LGA 1851 to be installed together
  • Consistency: The suggestion AI and the improvement AI use the same rules
  • Cleanup: Removed extra code for simplicity

Better code

  • Clear variable names: More understandable variable naming
  • Readable code: Wrote clear, easy-to-understand code
  • Consistent comments: Wrote comments to a standard

[1.6.3] - 2025-10-13 (Smarter AI)

The AI understands better

  • Simple rules: Merged all rules so the AI does not get confused
  • No repetition: Removed duplicate information so the AI reads faster
  • Concise instructions: Shortened the AI instructions from long-winded to concise

REFERENCE MATERIAL improvements

  • Detailed configurations: Replaced the short reference material with detailed configurations for each budget segment
  • Multi-platform support: Provides configurations for both Intel and AMD in every segment
  • Clear classification:
    • OFFICE & STUDY: 2 segments (10-15M, 15-20M VND)
    • GAMING: 6 segments from 15M to 150M+ VND with specific configurations
    • GRAPHICS: 5 special segments for design/rendering (15M-200M+ VND)
  • Removed special characters: Avoids encoding errors, ensuring accurate display

AI logic improvements

  • PRIORITY RULES: Moved the important rules to the top of the instructions so the AI reads them first
  • Information integration: Merged customer information and the budget breakdown into the rules
  • Consistent logic: The AI clearly understands the link between budget and the component selection rules

Technical improvements

  • Optimised AI structure: Optimised the instruction structure to reduce confusion and increase accuracy
  • Cache optimisation: Clears the cache after each change to check immediately
  • Error prevention: Fixed the special characters that caused errors

[1.6.2] - 2025-10-12 (Better AI understanding & budget adjustment)

AI improvements

  • Separated rules: Separated the priority rules for the processor and the graphics card so the AI understands them more easily
  • Processor tiers by budget: Detailed guidance for choosing a processor (Intel Core i9/i7/i5/i3 and AMD Ryzen 9/7/5/3) for each budget level (70M+, 50-70M, 30-50M, under 30M VND)
  • Fine-tuned budget allocation: Re-adjusted the percentage for each component so the AI builds a more balanced configuration
  • Improved stability: Updated the version to make sure the AI always uses the latest rules

[1.6.1] - 2025-10-11 (AI auto-detection + product connection fix)

New feature - AI automatic step detection

  • Automatic step detection system: The plugin automatically detects and assigns IDs to the PC building steps

    • Keyword search: Automatically detects 11 component types through keywords (cpu, gpu, psu, cooling...)
    • One-click sync: The administrator only needs to press 1 "Đồng bộ" (Sync) button after setting up the steps
    • Flexible operation: No need to hardcode step IDs, and the plugin works on any website
    • Classification confidence: Divided into confidence levels (high, manual, none)
    • Manual adjustment: Allows the administrator to assign special cases by hand
  • Synchronised admin interface:

    • The "Đồng bộ AI" (AI Sync) menu in the Build PC admin section
    • Shows the current connection status
    • An information table: Step ID, Step name, Confidence
    • A "Đồng bộ ngay" (Sync now) button with a loading effect
    • A "Xóa kết nối" (Clear connection) button to reset and sync again
    • Automatically reloads the page after a successful sync

AI product connection improvements

  • Mainboard connection fix:

    • Fixed the AI key bug: 'main' and 'mainboard' are now compatible with each other
    • Updated the AI admin interface to match the step detection system
    • Priority order: the 'main' key is preferred for mainboard
    • Improved keyword search for accented Vietnamese
  • System optimisation:

    • Removed hardcoding: No longer depends on hardcoded values, relying entirely on the step detection system
    • Auto sync when needed: Automatically syncs the system when it is not ready
    • Better error handling: Detailed logging and clear error messages
    • Fixed database queries: Fixed a table name error when querying the database
    • Attach step IDs: Every AI suggestion has an accurate step ID
  • Simplified interface:

    • Removed hardcoded step IDs: No more hardcoded step IDs (193-203) in JavaScript
    • Simple connection logic: The interface only needs a basic connection to be compatible
    • Step ID from the system: Relies on the step_id from the suggestion response
    • Better error handling: A better fallback when a step connection is not found

Admin interface improvements

  • Form submission fix:

    • Fixed a button click not triggering form submission (browser compatibility)
    • Improved the loading overlay with a fallback creation
    • Improved AJAX error handling and user feedback
    • Added comprehensive debug tools for troubleshooting
  • Step management:

    • Fixed step creation to use a script template instead of an incomplete hidden template
    • All step fields now display correctly (order, categories, options, attributes)
    • Improved per-step form validation with detailed error messages
    • Automatically generates slugs for both builder and steps, supporting Vietnamese characters
  • Select2 integration:

    • Fixed the Select2 initialisation timing
    • Optimised the interface so the X button displays better (no overlapping text)
    • Improved the dropdown style with correct z-index management
    • Improved the category and attribute selection experience
  • Delete function:

    • Added the missing delete_builder() method in the admin class
    • Fixed the AJAX URL configuration for deleting a builder
    • Improved the confirmation dialog and error handling
    • Properly cleans up the related steps when deleting builders

UI improvements

  • CSS optimisation:

    • Cleaned CSS from 1143 down to 1075 lines, removing duplicates
    • Improved Select2 styling with higher priority
    • Improved the responsive design of admin forms
    • Better visual feedback for the loading state
  • JavaScript improvements:

    • Comprehensive error handling and debug logging
    • Improved event handling for dynamic content
    • Improved timing for dependent libraries (Select2, WordPress Media)
    • Better browser compatibility for form submission

Technical improvements

  • Compatibility checking rewritten (class-ai-product-mapper.php):

    • Removed hardcoding: No more hardcoded step connections in check_product_compatibility()
    • Uses the Step Mapper: Automatically detects the step type from the slug through the Step Mapper
    • Socket check: Checks processor ↔ main compatibility (Intel LGA 1700, AMD AM5...)
    • Memory type check: Checks memory ↔ main compatibility (DDR4, DDR5...)
    • Normalisation: Normalises socket and memory type names for accurate comparison
    • Safe when missing: If the Step Mapper has not been synced, it accepts all products (no blocking)
    • Detailed logging: Logs each check step for debugging
  • AI Prompt improvements (class-ai-core.php):

    • Strict product_id instructions: Added a bold warning that the AI must use the product_id from the list
    • Examples with real numbers: Replaced <ID> with concrete examples (77649, 88800) so the AI understands better
    • Verification step: Added a step to check the product_id before returning JSON
    • New version: v8-STRICT-PRODUCT-ID to clear the AI cache
  • Backward compatibility:

    • Kept the hardcoded connection for websites that have not synced
    • Automatically falls back if the Step Mapper is not ready
    • Does not affect websites running v1.6.0
    • get_mapping_summary(): Gets the overview for the admin interface
  • AJAX handling:

    • ajax_sync_steps: Performs the sync
    • ajax_get_mapping: Gets the connection status
    • ajax_clear_mapping: Clears the connection and resets
  • New files:

    • admin-ai-sync.css: Styling for the admin page
    • admin-ai-sync.js: AJAX logic and interface interaction

Other updates

  • AI Handler: Prefers the Step Mapper, falling back to hardcoding
  • Logging: Added logs that distinguish Step Mapper vs Legacy mode
  • Database: Saves the connection in wp_options (key: tandoanh_buildpc_ai_step_mapping)

Upgrading from v1.6.0

  1. Update the plugin to v1.6.1
  2. Go to "Tân Doanh Build PC" → "Đồng bộ AI" (AI Sync)
  3. Press the "Đồng bộ ngay" (Sync now) button
  4. Confirm that all Steps have been recognised (shown in green)
  5. From now on the AI uses the Step Mapper automatically

Global deployment

The plugin now works on EVERY WordPress/WooCommerce website without needing:

  • Hardcoded step IDs
  • Code edits after installation
  • Knowing the database structure beforehand

Standard procedure:

  1. Install the plugin
  2. Create a Builder and the Steps (processor, graphics card, memory...)
  3. Press "Đồng bộ AI" (AI Sync)
  4. Done - the AI works immediately

[1.6.0] - 2025-10-10 (Integrated AI advisor - STABLE VERSION)

New feature - AI advisor integrated with Gemini 2.0 Flash

  • Integrated smart AI advisor: Automatically advises on building a PC using Google Gemini AI

    • Analyses the budget and the intended use
    • Suggests an optimal configuration with 8 complete components
    • Automatically checks compatibility between components
    • Explains the reason for choosing each product
  • Smart product connection:

    • Automatically connects AI suggestions to real products in the database
    • Handles the power supply and cooler with 2 types: air cooler and liquid cooler
    • Supports connecting multiple categories
  • Smart cache:

    • Product cache of 15 minutes to optimise speed
    • Automatically refreshes when there are new products
    • Version control with a timestamp to clear the cache when needed

System improvements

  • Accurate step connections:

    • Fixed the power supply connection: psu-nguon-may-tinh → psu-bo-nguon (Step 199)
    • Fixed the cooler connection: Supports both air coolers (Step 201) and liquid coolers (Step 202)
    • Added connections for storage (Step 198) and monitor (Step 203)
  • Optimised AI instructions with smart budget allocation:

    • Instructs the AI to choose exactly the 8 required components
    • Calculates the specific budget in VND (not only %)
    • Automatic budget segments:
      • High-end (40M+): graphics card 40-45% (RTX 4070+ REQUIRED), liquid cooler 6-10% (3-5M)
      • Mid-high (25-40M): graphics card 38-42%, cooler 4-7% (liquid or air cooler depending on the processor)
      • Mainstream (<25M): graphics card 35-40%, air cooler 3-5%
    • Graphics card priority: Prioritises a high-end graphics card, adjusting automatically if the total is < 90%
    • Liquid cooler required for 40M+ builds: Ensures the cooler is strong enough for a high-end configuration
    • Checks processor/main socket compatibility
    • Calculates the power supply wattage from the graphics card (4060→650W, 4070→750W, 4080→850W)
  • 6-step component selection strategy:

    1. Determine the segment by budget
    2. Start from the graphics card - choose the most expensive card within the % range
    3. Choose a processor that suits the graphics card
    4. Choose the remaining components by %
    5. Check whether the total reaches 90-95%
    6. If < 90% → Choose a more expensive graphics card/processor again
  • Advanced debugging & logging:

    • Detailed logging of product keys and counts
    • Warns when the power supply or cooler is missing
    • Product price statistics (min/max/avg) for each category
    • Checks that a power supply/cooler exists before sending to the AI
  • Category filtering:

    • Supports excluding categories through the setting tandoanh_buildpc_ai_exclude_categories
    • Automatically removes out-of-stock products and products priced at 0 VND
    • Filters out discontinued products

Serious bug fixes

  • Fixed the AI repeating itself: The AI no longer re-asks questions that have been answered

    • Added a status indicator to keep track
    • Automatically detects the current step
    • Principle: DO NOT ASK AGAIN for information already given
  • Fixed automatic suggestions:

    • Automatically loads products when the user confirms
    • Scans the history to find a reference case
    • Fixed the isFinalConfirmation regex
  • Fixed the AI conversation flow: The AI extracts the right information from the chat history

    • Added the method extract_info_from_history() to analyse history automatically
    • Extracts the purpose, budget and reference case from the conversation
    • Injects the extracted text into the system instructions
  • Fixed the AI Chat Widget display:

    • The widget shows only when the AI is enabled and has a valid API key
    • Fixed the AJAX connection so chat can send messages successfully
    • Uses the correct global variable tandoanh_buildpc_ajax

New files

  • includes/ai/class-ai-core.php - The main AI engine
  • includes/ai/class-ai-ajax-handler.php - AJAX handling
  • includes/ai/class-ai-product-mapper.php - Connects AI suggestions with products
  • includes/ai/class-ai-prompt-builder.php - Builds the instructions for the AI
  • includes/ai/init.php - Initialises the AI module
  • assets/js/ai-chat-manager.js - Chat interface
  • assets/css/ai-chat.css - Chat styling

Documentation

  • includes/ai/README.md - Guide to using the AI module
  • includes/ai/QUICKSTART.md - Quick start guide
  • includes/ai/TESTING.md - Testing guide
  • includes/ai/SUMMARY.md - Technical summary
  • includes/ai/FIX-*.md - Fix documentation for individual issues

System requirements

Test results - Smart budget allocation

Test case: "Gaming 50M, white case with LED lighting"

  • OK: graphics card: MSI GeForce RTX 5070 Ti 16GB (26.5M / 53.7%) - Excellent!
  • OK: processor: Intel Core i5-14500 (5.85M / 11.9%)
  • OK: cooler: DeepCool LT720 360mm liquid cooler (2.69M / 5.5%) - Correctly a liquid cooler!
  • OK: main: ASUS PRIME B860M-A (4.69M / 9.5%)
  • OK: memory: Corsair Vengeance LED 64GB (5.25M / 10.6%)
  • OK: storage: WD Blue 1TB NVMe (1.75M / 3.5%)
  • OK: power supply: Corsair CX750 750W (1.49M / 3.0%)
  • OK: case: MSI PAG PANO M110A White (1.09M / 2.2%)
  • Total: 49,310,000 VND (98.6% of the budget) ← Nearly perfect!

Results:

  • OK: All 8 components present, no missing power supply/cooler
  • OK: A high-end graphics card that fits the 40M+ segment
  • OK: The liquid cooler meets the requirement (not an air cooler)
  • OK: The total price is close to the budget (90-95% target: 45-47.5M, actual: 49.3M)
  • OK: The AI prioritises the graphics card per the strategy (53.7% of the budget)

Future improvements

  • Add a backup product cache as a fallback
  • Optimise the instructions to reduce token usage
  • Support multiple AI models (Claude, GPT)
  • Multi-language support for AI responses
  • Fine-tune the memory allocation (64GB may be too high for some cases)

[1.5.2] - 2025-10-09

Added

  • Optimised the speed of opening the product picker modal: uses a preloading technique when the user hovers over the product picker button.
  • Applied the "show stale data while loading new data" strategy so the modal opens immediately with the data already available while loading new data in the background, reducing the waiting time.

Fixes

  • Fixed a serious bug that made the "Sắp xếp" (Sort) function in the modal work only on the second click. The bug came from the interface reset not firing the event correctly.
  • Fixed the filter not being reset correctly when re-editing an already chosen step.

Changed

  • Updated the plugin to be compatible with WordPress 6.8.3.
  • Performance optimisation: Added width and height attributes to all <img> tags generated by PHP to improve the Cumulative Layout Shift (CLS) score.

[1.5.1] - 2025-10-09

Added

  • Added the file assets/css/mobile.css to manage responsive rules separately.
  • Added "mobile-first" media queries to optimise display on phones and tablets, based on responsive-guidelines.md.

Changed

  • Restructured the main interface, the modal and the configuration summary table to ensure compatibility on small screens.
  • Improved the layout of the product list and filters to avoid broken layouts on mobile.

Fixes

  • Fixed several display bugs that broke the interface on small-screen devices.

[1.5.0] - 2025-10-08

Added

  • A frontend email sending module (assets/js/email.js) with a quick modal, HTML preview and interface states.
  • Sends a summary email in HTML instead of only attaching a PDF; supports an inline QR code in the email.
  • Server-side QR code generation (includes/share.php) returning qr_png_dataurl when possible.
  • The function buildSummaryHtml(shareUrl, qrDataUrl) in assets/js/create-pdf.js to create an email-compatible HTML summary.
  • A sample HTML preview preview-email.html to check before sending.

Changed

  • Updated the mail sending flow: the modal opens quickly, share/QR generation runs asynchronously, and the email content uses a table layout to be compatible with Outlook/Gmail.

Fixes

  • Improved QR code display in email (temporarily allows data: URLs when sanitising; suggests switching to CID inline attachments if an email client strips data URLs).

[1.4.9] - 2025-10-07

Changed

  • A light interface upgrade for the filters/modal to reduce jitter when Select2 initialises (reserving height, stabilising the interface).

Fixes

  • Fixed a JavaScript runtime error in app.js that stopped execution (notification handling) - avoiding blocking other handlers.
  • Improved the product editing flow: when the server returns selectedProduct without product.html, the client applies the server data immediately to avoid showing Đang tải... (Loading...) until F5.
  • Fixed a typo causing a ReferenceError in modal-manager.js (affecting the "Thêm vào giỏ hàng" (Add to cart) function).

[1.4.8] - 2025-10-06

Changed

  • Changed how the edit modal works: when editing, the interface sends detailed information about what needs to change to the server; the server recalculates the constraints based on the current state
  • Filters skip the cache when the edit modal opens to ensure the latest information is always shown
  • Optimisation: gets the selected product information from the interface when the modal opens to reduce conflicts with session management

Fixes

  • Avoids showing old filters/products after opening the modal without needing F5.

[1.4.7] - 2025-10-06

Changed

  • The server returns filter data with dependencies (the server is the authoritative source for hierarchical filters).
  • Reinitialises Select2 safely when needed.

Fixes

  • Fixed the dropdown not restoring when clicking "Tất cả" (All) (clear filters).
  • Reduced race conditions between AJAX calls when loading filters/products.

[1.4.6] - 2025-10-05

Added

  • Split the Share feature into includes/share.php and assets/js/share.js.

Changed

  • Optimised the component interface (modularization).

Fixes

  • Some visual bugs and duplicated rulesets.

[1.4.5] - 2025-10-04

Fixes

  • Invalidate the transient cache when selected_products changes.
  • Restored the caching mechanism for get_products with a reasonable TTL (HTML 30s, IDs 1h).
  • Reduced server log noise by guarding error_log behind a debug flag.

[1.4.4] - 2025-10-04

Fixes

  • Debug cleanup: turned off the debug console and server logs by default; moved verbose debug into the flag TANDOANH_BUILDPC_DEBUG_LOGS.

[1.4.3] - 2025-10-04

Fixes

  • Fixed the selected_products override logic to avoid accidental overrides.

[1.4.2] - 2025-10-04

Fixes

  • Fixed the total price not updating after a session restore.
  • Improved Select2 and modal behaviour.

[1.4.1] - 2025-10-03

Fixes

  • Fixed an edge case of duplicated selections when restoring a session.

[1.4.0] - 2025-10-02

Added

  • Interface refactor: split the interface into components, reducing size and improving maintainability.
  • Improved the filter logic to prevent cross-effects between steps.

[1.3.0] - 2025-09-15

Changed

  • Improved session management: saves build progress across multiple tabs, persistent sessions, and safe going back when errors occur.
  • Optimised the filter cache: filters persist across reloads, reducing the number of AJAX calls.
  • Faster selecting/removing of products in the modal, with instant feedback.
  • Optimised memory use, cutting memory usage by 40% when building many steps.

Fixes

  • Fixed session conflicts when operating several tabs at the same time.
  • Fixed the modal not closing immediately when selecting/removing a product.

[1.2.0] - 2025-09-02

Added

  • Advanced dependency system: filters and products are calculated based on linked attributes between steps (socket, memory, chipset...).
  • Lets you set component compatibility rules for each builder.
  • Optimised filter queries, reducing the load on the database.

Changed

  • Optimised the filter logic: each step has an independent filter state, not affected by other steps.
  • Improved AJAX performance, reducing the response time of filters/products.

Fixes

  • Fixed filters getting "contaminated" when switching steps continuously.
  • Fixed products displaying incorrectly when filter attributes change.

[1.1.0] - 2025-08-20

Added

  • Mobile-first responsive design and a touch-friendly interface.

Fixes

  • Some browser compatibility issues and performance optimisation on mobile devices.

[1.0.0] - 2025-09-18

Added

  • Official release (Stable Release): a step-by-step PC building interface, advanced product filtering, smart dependency checking, session management and cart integration.

[1.0-beta] - 2025-07-06

Added

  • The first beta version: core functionality, basic UI/UX, initial testing.

Contact information


The changelog is maintained to the "Keep a Changelog" standard (written in Vietnamese).

Short conventions:

  • Each entry starts with: "## [X.Y.Z] - YYYY-MM-DD"
  • Sections: Added / Changed / Fixes / Removed / Security
  • Be concise and do not include detailed internal debugging.