chore: update project documentation, audit reports, and initialize IDE configuration files
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
# Accessibility Code Fixes - Detailed Implementation Guide
|
||||
# Sửa Lỗi Trợ Năng - Hướng Dẫn Triển Khai Chi Tiết
|
||||
|
||||
## Fix #1: File Upload Input aria-label
|
||||
## Sửa #1: aria-label cho Input Tải Tệp Lên
|
||||
|
||||
**File**: `apps/web/components/listings/image-upload.tsx`
|
||||
**Line**: 118
|
||||
**Tệp**: `apps/web/components/listings/image-upload.tsx`
|
||||
**Dòng**: 118
|
||||
|
||||
### Before
|
||||
### Trước
|
||||
```tsx
|
||||
<input
|
||||
ref={inputRef}
|
||||
@@ -20,7 +20,7 @@
|
||||
/>
|
||||
```
|
||||
|
||||
### After
|
||||
### Sau
|
||||
```tsx
|
||||
<input
|
||||
ref={inputRef}
|
||||
@@ -36,16 +36,16 @@
|
||||
/>
|
||||
```
|
||||
|
||||
**Why**: Hidden inputs need aria-label so screen readers can announce their purpose when focused.
|
||||
**Lý do**: Các input ẩn cần aria-label để trình đọc màn hình có thể thông báo mục đích của chúng khi được lấy tiêu điểm.
|
||||
|
||||
---
|
||||
|
||||
## Fix #2: Search Dialog Input aria-label
|
||||
## Sửa #2: aria-label cho Input Hộp Thoại Tìm Kiếm
|
||||
|
||||
**File**: `apps/web/app/[locale]/(public)/search/page.tsx`
|
||||
**Line**: 189
|
||||
**Tệp**: `apps/web/app/[locale]/(public)/search/page.tsx`
|
||||
**Dòng**: 189
|
||||
|
||||
### Before
|
||||
### Trước
|
||||
```tsx
|
||||
<input
|
||||
type="text"
|
||||
@@ -58,7 +58,7 @@
|
||||
/>
|
||||
```
|
||||
|
||||
### After
|
||||
### Sau
|
||||
```tsx
|
||||
<input
|
||||
type="text"
|
||||
@@ -72,16 +72,16 @@
|
||||
/>
|
||||
```
|
||||
|
||||
**Why**: Text inputs need aria-label when no associated label element exists. Placeholder is not a substitute for aria-label.
|
||||
**Lý do**: Các input văn bản cần aria-label khi không có phần tử nhãn liên kết. Placeholder không thể thay thế cho aria-label.
|
||||
|
||||
---
|
||||
|
||||
## Fix #3: Admin Moderation - Select All Checkbox
|
||||
## Sửa #3: Quản Trị Kiểm Duyệt - Checkbox Chọn Tất Cả
|
||||
|
||||
**File**: `apps/web/app/[locale]/(admin)/admin/moderation/page.tsx`
|
||||
**Line**: 222
|
||||
**Tệp**: `apps/web/app/[locale]/(admin)/admin/moderation/page.tsx`
|
||||
**Dòng**: 222
|
||||
|
||||
### Before
|
||||
### Trước
|
||||
```tsx
|
||||
<TableHead className="w-10">
|
||||
<input
|
||||
@@ -93,7 +93,7 @@
|
||||
</TableHead>
|
||||
```
|
||||
|
||||
### After
|
||||
### Sau
|
||||
```tsx
|
||||
<TableHead className="w-10">
|
||||
<input
|
||||
@@ -106,16 +106,16 @@
|
||||
</TableHead>
|
||||
```
|
||||
|
||||
**Why**: Checkbox in table headers need aria-label to distinguish them from row checkboxes.
|
||||
**Lý do**: Checkbox trong tiêu đề bảng cần aria-label để phân biệt với các checkbox trên từng hàng.
|
||||
|
||||
---
|
||||
|
||||
## Fix #4: Admin Moderation - Row Checkboxes
|
||||
## Sửa #4: Quản Trị Kiểm Duyệt - Checkbox Từng Hàng
|
||||
|
||||
**File**: `apps/web/app/[locale]/(admin)/admin/moderation/page.tsx`
|
||||
**Line**: 242
|
||||
**Tệp**: `apps/web/app/[locale]/(admin)/admin/moderation/page.tsx`
|
||||
**Dòng**: 242
|
||||
|
||||
### Before
|
||||
### Trước
|
||||
```tsx
|
||||
<TableCell>
|
||||
<input
|
||||
@@ -127,7 +127,7 @@
|
||||
</TableCell>
|
||||
```
|
||||
|
||||
### After (Option 1 - Simple)
|
||||
### Sau (Phương án 1 - Đơn giản)
|
||||
```tsx
|
||||
<TableCell>
|
||||
<input
|
||||
@@ -140,7 +140,7 @@
|
||||
</TableCell>
|
||||
```
|
||||
|
||||
### After (Option 2 - Better with title)
|
||||
### Sau (Phương án 2 - Tốt hơn với tiêu đề)
|
||||
```tsx
|
||||
<TableCell>
|
||||
<input
|
||||
@@ -153,30 +153,30 @@
|
||||
</TableCell>
|
||||
```
|
||||
|
||||
**Why**: Each checkbox needs unique aria-label that includes context about what listing it represents.
|
||||
**Lý do**: Mỗi checkbox cần aria-label duy nhất bao gồm ngữ cảnh về tin đăng mà nó đại diện.
|
||||
|
||||
---
|
||||
|
||||
## Fix #5: Test Mock Image Component
|
||||
## Sửa #5: Component Ảnh Giả Lập trong Kiểm Thử
|
||||
|
||||
**File**: `apps/web/app/[locale]/(public)/search/__tests__/search.spec.tsx`
|
||||
**Line**: 46
|
||||
**Tệp**: `apps/web/app/[locale]/(public)/search/__tests__/search.spec.tsx`
|
||||
**Dòng**: 46
|
||||
|
||||
### Before
|
||||
### Trước
|
||||
```tsx
|
||||
vi.mock('next/image', () => ({
|
||||
default: (props: Record<string, unknown>) => <img {...props} />,
|
||||
}));
|
||||
```
|
||||
|
||||
### After (Option 1 - Simple)
|
||||
### Sau (Phương án 1 - Đơn giản)
|
||||
```tsx
|
||||
vi.mock('next/image', () => ({
|
||||
default: (props: Record<string, unknown>) => <img {...props} alt={props.alt || ''} />,
|
||||
}));
|
||||
```
|
||||
|
||||
### After (Option 2 - With Warning)
|
||||
### Sau (Phương án 2 - Có Cảnh Báo)
|
||||
```tsx
|
||||
vi.mock('next/image', () => ({
|
||||
default: (props: Record<string, unknown>) => {
|
||||
@@ -188,16 +188,16 @@ vi.mock('next/image', () => ({
|
||||
}));
|
||||
```
|
||||
|
||||
**Why**: Mock should enforce alt attribute to catch missing alts in tests before production.
|
||||
**Lý do**: Bản giả lập nên bắt buộc thuộc tính alt để phát hiện các ảnh thiếu alt trong kiểm thử trước khi đưa lên môi trường production.
|
||||
|
||||
---
|
||||
|
||||
## Fix #6 (Enhancement): Image Upload Drag-Drop Accessibility
|
||||
## Sửa #6 (Cải Tiến): Trợ Năng Kéo-Thả Tải Ảnh Lên
|
||||
|
||||
**File**: `apps/web/components/listings/image-upload.tsx`
|
||||
**Lines**: 86-128
|
||||
**Tệp**: `apps/web/components/listings/image-upload.tsx`
|
||||
**Dòng**: 86-128
|
||||
|
||||
### Current Code
|
||||
### Mã Hiện Tại
|
||||
```tsx
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
@@ -230,7 +230,7 @@ vi.mock('next/image', () => ({
|
||||
</div>
|
||||
```
|
||||
|
||||
### Enhanced Code
|
||||
### Mã Đã Cải Tiến
|
||||
```tsx
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
@@ -273,82 +273,81 @@ vi.mock('next/image', () => ({
|
||||
</div>
|
||||
```
|
||||
|
||||
**Changes**:
|
||||
- `role="button"` - Identifies div as an interactive button
|
||||
- `tabIndex={0}` - Makes div keyboard accessible
|
||||
- `aria-label` - Describes the purpose to screen readers
|
||||
- `onKeyDown` handler - Allows Enter/Space to activate
|
||||
- `focus-visible` styles - Shows focus indicator for keyboard navigation
|
||||
**Các thay đổi**:
|
||||
- `role="button"` - Xác định div là một nút tương tác
|
||||
- `tabIndex={0}` - Cho phép div nhận tiêu điểm từ bàn phím
|
||||
- `aria-label` - Mô tả mục đích cho trình đọc màn hình
|
||||
- Trình xử lý `onKeyDown` - Cho phép kích hoạt bằng Enter/Space
|
||||
- Kiểu `focus-visible` - Hiển thị chỉ báo tiêu điểm khi điều hướng bằng bàn phím
|
||||
|
||||
**Why**: Makes drag-drop area fully keyboard accessible for users who can't use a mouse.
|
||||
**Lý do**: Làm cho vùng kéo-thả có thể truy cập hoàn toàn bằng bàn phím cho những người dùng không thể sử dụng chuột.
|
||||
|
||||
---
|
||||
|
||||
## Testing After Implementation
|
||||
## Kiểm Tra Sau Khi Triển Khai
|
||||
|
||||
### 1. Screen Reader Testing
|
||||
### 1. Kiểm Tra Trình Đọc Màn Hình
|
||||
```bash
|
||||
# Use VoiceOver (Mac), NVDA (Windows), or JAWS
|
||||
# Navigate to each fixed element and verify:
|
||||
# - Input is announced with its aria-label
|
||||
# - Checkbox is announced with its aria-label
|
||||
# - Purpose is clear from screen reader announcement
|
||||
# Sử dụng VoiceOver (Mac), NVDA (Windows), hoặc JAWS
|
||||
# Điều hướng đến từng phần tử đã sửa và xác nhận:
|
||||
# - Input được thông báo với aria-label của nó
|
||||
# - Checkbox được thông báo với aria-label của nó
|
||||
# - Mục đích rõ ràng qua thông báo của trình đọc màn hình
|
||||
```
|
||||
|
||||
### 2. Keyboard Navigation Testing
|
||||
### 2. Kiểm Tra Điều Hướng Bằng Bàn Phím
|
||||
```bash
|
||||
# Tab through the page
|
||||
# Verify:
|
||||
# - All interactive elements are reachable via Tab
|
||||
# - Focus is visible on all elements
|
||||
# - Enter/Space activates buttons and checkboxes
|
||||
# - Image upload drag area is focused and can be activated with keyboard
|
||||
# Tab qua trang
|
||||
# Xác nhận:
|
||||
# - Tất cả các phần tử tương tác có thể truy cập bằng Tab
|
||||
# - Tiêu điểm hiển thị rõ trên tất cả các phần tử
|
||||
# - Enter/Space kích hoạt các nút và checkbox
|
||||
# - Vùng kéo-thả tải ảnh có thể nhận tiêu điểm và kích hoạt bằng bàn phím
|
||||
```
|
||||
|
||||
### 3. Automated Testing
|
||||
### 3. Kiểm Tra Tự Động
|
||||
```bash
|
||||
# Run axe
|
||||
# Chạy axe
|
||||
npm run test:a11y
|
||||
|
||||
# Or use Lighthouse
|
||||
# Hoặc dùng Lighthouse
|
||||
npx lighthouse https://localhost:3000 --view
|
||||
|
||||
# ESLint JSX Accessibility Plugin should catch these issues:
|
||||
# Plugin ESLint JSX Accessibility nên phát hiện các vấn đề này:
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### 4. Visual Testing
|
||||
### 4. Kiểm Tra Trực Quan
|
||||
```bash
|
||||
# Verify with browser dev tools:
|
||||
# - Inspect each input to confirm aria-label attribute exists
|
||||
# - Check for proper focus styles
|
||||
# - Verify focus ring colors meet contrast requirements
|
||||
# Xác nhận bằng công cụ dành cho nhà phát triển trong trình duyệt:
|
||||
# - Kiểm tra từng input để xác nhận thuộc tính aria-label tồn tại
|
||||
# - Kiểm tra kiểu tiêu điểm phù hợp
|
||||
# - Xác nhận màu vòng tiêu điểm đáp ứng yêu cầu độ tương phản
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary of Changes
|
||||
## Tóm Tắt Các Thay Đổi
|
||||
|
||||
| Issue | File | Line | Type | Severity |
|
||||
|-------|------|------|------|----------|
|
||||
| File input missing aria-label | image-upload.tsx | 118 | aria-label | HIGH |
|
||||
| Search input missing aria-label | search/page.tsx | 189 | aria-label | HIGH |
|
||||
| Header checkbox missing aria-label | moderation/page.tsx | 222 | aria-label | HIGH |
|
||||
| Row checkboxes missing aria-label | moderation/page.tsx | 242 | aria-label | HIGH |
|
||||
| Mock Image missing alt | search.spec.tsx | 46 | alt attribute | MEDIUM |
|
||||
| Drag-drop area not keyboard accessible | image-upload.tsx | 86-128 | enhancement | MEDIUM |
|
||||
| Vấn đề | Tệp | Dòng | Loại | Mức độ nghiêm trọng |
|
||||
|--------|-----|------|------|---------------------|
|
||||
| Input tệp thiếu aria-label | image-upload.tsx | 118 | aria-label | CAO |
|
||||
| Input tìm kiếm thiếu aria-label | search/page.tsx | 189 | aria-label | CAO |
|
||||
| Checkbox tiêu đề thiếu aria-label | moderation/page.tsx | 222 | aria-label | CAO |
|
||||
| Checkbox hàng thiếu aria-label | moderation/page.tsx | 242 | aria-label | CAO |
|
||||
| Ảnh giả lập thiếu alt | search.spec.tsx | 46 | thuộc tính alt | TRUNG BÌNH |
|
||||
| Vùng kéo-thả không thể truy cập bằng bàn phím | image-upload.tsx | 86-128 | cải tiến | TRUNG BÌNH |
|
||||
|
||||
---
|
||||
|
||||
## Estimated Implementation Time
|
||||
## Thời Gian Triển Khai Ước Tính
|
||||
|
||||
- Fix #1: 2 minutes
|
||||
- Fix #2: 2 minutes
|
||||
- Fix #3: 2 minutes
|
||||
- Fix #4: 3 minutes (need to find item.title in context)
|
||||
- Fix #5: 2 minutes
|
||||
- Fix #6: 10 minutes
|
||||
- Testing: 15-20 minutes
|
||||
|
||||
**Total: ~35-45 minutes**
|
||||
- Sửa #1: 2 phút
|
||||
- Sửa #2: 2 phút
|
||||
- Sửa #3: 2 phút
|
||||
- Sửa #4: 3 phút (cần tìm item.title trong ngữ cảnh)
|
||||
- Sửa #5: 2 phút
|
||||
- Sửa #6: 10 phút
|
||||
- Kiểm tra: 15-20 phút
|
||||
|
||||
**Tổng cộng: ~35-45 phút**
|
||||
|
||||
Reference in New Issue
Block a user