All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m12s
User feedback 2026-05-07 (annotation screenshot):
1. "Lưu" thay "Lưu (đóng)" — KHÔNG đóng workspace, chỉ toast + invalidate sync
2. Thêm nút "Xóa phiếu" bottom — CHỈ Bản nháp (DangSoanThao), KHÔNG xóa Trả lại
(đã có lịch sử workflow). Soft-delete (AuditableEntity IsDeleted=true,
không xóa hoàn toàn DB).
3. Bỏ nút "Sửa header" + "Đóng" + "Xóa" header bar workspace mode (chuyển
xuống bottom action bar). Header bar chỉ còn nhóm display info + nút "Đóng"
cho non-workspace view (Danh sách / Duyệt readOnly).
4. Section 4 column header NCC: dùng s.supplierName (master) thay vì
displayName ?? supplierName (custom). displayName fallback sang title tooltip.
5. Section 3 row Xóa: nếu NCC đã có quotes (báo giá ở Section 4) → KHÔNG cho
xóa (tránh mất báo giá). Hiển thị icon disabled + tooltip "xóa báo giá
trước rồi mới xóa NCC".
Implementation:
~ PeDetailTabs.tsx (× 2 app)
- Header bar workspace mode actions: bỏ "Sửa header" Pencil button (có
inline edit Section 1 + pencil hover Panel 1 thay thế), bỏ "Xóa" (chuyển
xuống bottom). "Đóng" giữ chỉ cho readOnly + non-workspace view.
- useNavigate import bỏ (chỉ dùng còn ở CreateContractDialog scope local).
- Bottom action bar workspace + canEdit + !readOnly:
* LEFT: "Xóa phiếu" red button (chỉ phase === DangSoanThao) + confirm
dialog "soft-delete, không xóa hoàn toàn DB" + onDelete callback (existing
DELETE /pe/:id endpoint, AuditableEntity IsDeleted=true).
* CENTER: status text "✓ Các thay đổi đã tự động lưu khi chỉnh sửa..."
* RIGHT: "Lưu" ghost button → invalidate ['pe-detail', id] + ['pe-list']
+ toast "Đã lưu — sync server" (KHÔNG onBack — workspace stay open).
* RIGHT: "Lưu & Gửi Duyệt →" giữ nguyên (POST transitions).
- SuppliersTab row actions: hasQuotes computed (= ev.details.some(d =>
d.quotes.some(q => q.purchaseEvaluationSupplierId === s.id))). canDelete
= !isWinner && !hasQuotes. Render Trash button enabled vs disabled span
với tooltip "xóa báo giá trước".
- ItemsTab matrix column header: {s.supplierName} (was {s.displayName ??
s.supplierName}). title attr giữ displayName tooltip.
Verify: npm run build fe-admin + fe-user pass · 0 TS error · áp rule strict
verify khi remove import + button/condition logic changes.
UAT mode: skip dotnet test (FE-only), push ngay.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- @vitejs/plugin-react uses Oxc
- @vitejs/plugin-react-swc uses SWC
React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.
Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])