feat(web): add auth+search i18n translations and filter-bar accessibility

Add missing auth and search translation namespaces to vi.json and en.json
that are required by login/register pages and search filter-bar component.
Update filter-bar with useTranslations('search'), aria-labels, and
role="search" for WCAG 2.1 AA compliance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ho Ngoc Hai
2026-04-09 10:22:59 +07:00
parent 8179f1c16e
commit 862078df37
21 changed files with 213 additions and 83 deletions

View File

@@ -5,7 +5,6 @@ describe('ListingSoldListener', () => {
let mockCommandBus: { execute: ReturnType<typeof vi.fn> };
let mockPrisma: {
listing: { findUnique: ReturnType<typeof vi.fn> };
savedListing: { findMany: ReturnType<typeof vi.fn> };
};
let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn> };
@@ -13,7 +12,6 @@ describe('ListingSoldListener', () => {
mockCommandBus = { execute: vi.fn().mockResolvedValue(undefined) };
mockPrisma = {
listing: { findUnique: vi.fn() },
savedListing: { findMany: vi.fn().mockResolvedValue([]) },
};
mockLogger = { log: vi.fn(), warn: vi.fn() };
@@ -48,15 +46,12 @@ describe('ListingSoldListener', () => {
);
});
it('notifies watchers when listing is sold', async () => {
it('notifies only seller when listing is sold (no watcher support)', async () => {
mockPrisma.listing.findUnique.mockResolvedValue({
id: 'listing-1',
property: { title: 'Căn hộ đẹp' },
seller: { id: 'seller-1', email: 'seller@example.com' },
});
mockPrisma.savedListing.findMany.mockResolvedValue([
{ user: { id: 'watcher-1', email: 'watcher@example.com' } },
]);
await listener.handle({
aggregateId: 'listing-1',
@@ -66,8 +61,8 @@ describe('ListingSoldListener', () => {
occurredAt: new Date(),
});
// Seller + 1 watcher = 2 notifications
expect(mockCommandBus.execute).toHaveBeenCalledTimes(2);
// Only seller notification (savedListing model removed)
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
});
it('skips notification when listing not found', async () => {