feat(agents): add public agent profile page at /agents/[id]

Implements a public-facing agent profile page with:
- Backend: new GET /agents/:agentId/profile public API endpoint with
  agent info, active listings, quality score, and review stats
- Frontend: server-rendered profile page with generateMetadata for SEO,
  JSON-LD structured data (RealEstateAgent schema), breadcrumbs
- Agent profile displays bio, service areas, quality score gauge,
  active listing cards, reviews with star ratings, and contact CTA
- Mobile responsive layout with sticky contact sidebar on desktop
- Vietnamese UI text throughout, consistent with existing patterns

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-11 00:16:19 +07:00
parent 37fab515b7
commit 62485fee98
13 changed files with 905 additions and 5 deletions

View File

@@ -0,0 +1,24 @@
import { Inject } from '@nestjs/common';
import { type IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import {
AGENT_REPOSITORY,
type AgentPublicProfileData,
type IAgentRepository,
} from '../../../domain/repositories/agent.repository';
import { GetAgentPublicProfileQuery } from './get-agent-public-profile.query';
@QueryHandler(GetAgentPublicProfileQuery)
export class GetAgentPublicProfileHandler
implements IQueryHandler<GetAgentPublicProfileQuery>
{
constructor(
@Inject(AGENT_REPOSITORY)
private readonly agentRepo: IAgentRepository,
) {}
async execute(
query: GetAgentPublicProfileQuery,
): Promise<AgentPublicProfileData | null> {
return this.agentRepo.getPublicProfile(query.agentId);
}
}

View File

@@ -0,0 +1,3 @@
export class GetAgentPublicProfileQuery {
constructor(public readonly agentId: string) {}
}