diff --git a/src/features/login/components/AuthInitializer.tsx b/src/features/login/components/AuthInitializer.tsx index 93dcfd6..4df399f 100644 --- a/src/features/login/components/AuthInitializer.tsx +++ b/src/features/login/components/AuthInitializer.tsx @@ -20,7 +20,7 @@ export function AuthInitializer({ children }: { children: React.ReactNode }) { try { await loginService.refreshToken(); - const profile = await profileService.getMe(); + const profile = await profileService.obterColaboradorAtual(); setUser(mapToSafeProfile(profile)); } catch (error) { console.warn('Sessão expirada ou inválida', error); diff --git a/src/features/login/hooks/useAuth.ts b/src/features/login/hooks/useAuth.ts index 94c9032..07f38ab 100644 --- a/src/features/login/hooks/useAuth.ts +++ b/src/features/login/hooks/useAuth.ts @@ -16,7 +16,7 @@ export function useAuth() { mutationFn: loginService.login, onSuccess: async () => { try { - const profile = await profileService.getMe(); + const profile = await profileService.obterColaboradorAtual(); const safeProfile = mapToSafeProfile(profile); setUser(safeProfile); @@ -34,7 +34,7 @@ export function useAuth() { useQuery({ queryKey: ['auth-me'], queryFn: async () => { - const data = await profileService.getMe(); + const data = await profileService.obterColaboradorAtual(); const safeData = mapToSafeProfile(data); setUser(safeData); return safeData; diff --git a/src/features/login/interfaces/types.ts b/src/features/login/interfaces/types.ts index a6178bd..bd6f67e 100644 --- a/src/features/login/interfaces/types.ts +++ b/src/features/login/interfaces/types.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import type { ReactNode } from 'react'; -import type { UserProfile } from '../../profile/types'; +import type { UserProfileDto } from '../../profile/types'; import { loginSchema, @@ -38,9 +38,9 @@ export interface AuthLoginProps { } export interface AuthState { - user: UserProfile | null; + user: UserProfileDto | null; isAuthenticated: boolean; - setUser: (user: UserProfile | null) => void; + setUser: (user: UserProfileDto | null) => void; logout: () => void; hydrate: () => void; } diff --git a/src/features/login/utils/mappers.ts b/src/features/login/utils/mappers.ts index e130a0d..d1c2013 100644 --- a/src/features/login/utils/mappers.ts +++ b/src/features/login/utils/mappers.ts @@ -1,16 +1,22 @@ -import { UserProfile } from '../../profile/types'; +import { UserProfileDto } from '../../profile/types'; +import { Colaborador } from '../../profile/domain/Colaborador'; -export const mapToSafeProfile = (data: any): UserProfile => { +/** + * Converte Colaborador (entidade) ou objeto raw para UserProfileDto. + * Suporta ambos os formatos de propriedade (entidade DDD e DTO da API). + */ +export const mapToSafeProfile = (data: Colaborador | any): UserProfileDto => { return { matricula: data.matricula, userName: data.userName, nome: data.nome, codigoFilial: data.codigoFilial, nomeFilial: data.nomeFilial, - rca: data.rca, - discountPercent: data.discountPercent, - sectorId: data.sectorId, - sectorManagerId: data.sectorManagerId, - supervisorId: data.supervisorId, + rca: data.codigoRCA ?? data.rca, + discountPercent: data.percentualDesconto ?? data._percentualDesconto ?? data.discountPercent, + sectorId: data.codigoSetor ?? data.sectorId, + sectorManagerId: data.sectorManagerId ?? 0, + supervisorId: data.codigoSupervisor ?? data.supervisorId, }; }; + diff --git a/src/features/profile/components/ProfilePage.tsx b/src/features/profile/components/ProfilePage.tsx index b8061c5..b1a9b89 100644 --- a/src/features/profile/components/ProfilePage.tsx +++ b/src/features/profile/components/ProfilePage.tsx @@ -7,15 +7,23 @@ import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import CircularProgress from '@mui/material/CircularProgress'; import Alert from '@mui/material/Alert'; +import Avatar from '@mui/material/Avatar'; +import Chip from '@mui/material/Chip'; import { useAuthStore } from '../../login/store/useAuthStore'; import { useAuth } from '../../login/hooks/useAuth'; +import { Colaborador } from '../domain/Colaborador'; export default function ProfilePage() { const { useMe } = useAuth(); - const { data: profile, isLoading, error } = useMe(); + const { data: colaborador, isLoading, error } = useMe(); const user = useAuthStore((s) => s.user); - const displayData = profile || user; + // Converte o DTO para a entidade de domínio Colaborador + const displayData: Colaborador | null = colaborador + ? Colaborador.criarAPartirDoDto(colaborador) + : user + ? Colaborador.criarAPartirDoDto(user) + : null; if (isLoading) { return ( @@ -42,6 +50,47 @@ export default function ProfilePage() { return ( + {/* Header com Avatar e Nome */} + + + + + + {displayData.iniciais} + + + + {displayData.nome} + + + {displayData.nomeComFilial} + + + {displayData.ehRepresentanteComercial && ( + + )} + {displayData.podeAplicarDesconto && ( + + )} + + + + + + + + {/* Informações Pessoais */} @@ -78,6 +127,7 @@ export default function ProfilePage() { + {/* Informações Profissionais */} @@ -90,8 +140,7 @@ export default function ProfilePage() { Filial - {displayData.nomeFilial || '-'} ( - {displayData.codigoFilial || '-'}) + {displayData.nomeFilial || '-'} ({displayData.codigoFilial || '-'}) @@ -99,16 +148,16 @@ export default function ProfilePage() { RCA - {displayData.rca || '-'} + {displayData.ehRepresentanteComercial ? displayData.codigoRCA : 'Não é vendedor'} - {displayData.discountPercent !== undefined && ( + {displayData.podeAplicarDesconto && ( - Desconto (%) + Desconto Disponível - {displayData.discountPercent}% + {displayData.percentualDesconto}% )} @@ -117,9 +166,9 @@ export default function ProfilePage() { - {(displayData.sectorId !== undefined || - displayData.sectorManagerId !== undefined || - displayData.supervisorId !== undefined) && ( + {/* Informações Adicionais */} + {(displayData.codigoSetor !== undefined || + displayData.codigoSupervisor !== undefined) && ( @@ -127,23 +176,23 @@ export default function ProfilePage() { Informações Adicionais - {displayData.sectorId !== undefined && ( + {displayData.codigoSetor !== undefined && ( Setor - {displayData.sectorId} + {displayData.codigoSetor} )} - {displayData.supervisorId !== undefined && ( + {displayData.codigoSupervisor !== undefined && ( Supervisor - {displayData.supervisorId} + {displayData.codigoSupervisor} )} diff --git a/src/features/profile/domain/Colaborador.ts b/src/features/profile/domain/Colaborador.ts new file mode 100644 index 0000000..0de80b3 --- /dev/null +++ b/src/features/profile/domain/Colaborador.ts @@ -0,0 +1,71 @@ +import { UserProfileDto } from '../types'; + +/** + * Entidade de Domínio: Colaborador + */ +export class Colaborador { + private constructor( + public readonly matricula: number, + public readonly userName: string, + public readonly nome: string, + public readonly codigoFilial: string, + public readonly nomeFilial: string, + public readonly codigoRCA: number, + private readonly _percentualDesconto: number, + public readonly codigoSetor: number, + public readonly codigoSupervisor: number + ) {} + + // ========== FACTORY METHODS ========== + + static criarAPartirDoDto(dto: UserProfileDto): Colaborador { + return new Colaborador( + dto.matricula, + dto.userName, + dto.nome, + dto.codigoFilial, + dto.nomeFilial, + dto.rca, + dto.discountPercent, + dto.sectorId, + dto.supervisorId + ); + } + + // ========== COMPUTED PROPERTIES ========== + + get iniciais(): string { + if (!this.nome) return 'U'; + const partes = this.nome.trim().split(' ').filter(Boolean); + if (partes.length === 1) { + return partes[0][0].toUpperCase(); + } + return (partes[0][0] + partes[partes.length - 1][0]).toUpperCase(); + } + + get ehRepresentanteComercial(): boolean { + return this.codigoRCA > 0; + } + + get podeAplicarDesconto(): boolean { + return this._percentualDesconto > 0 && this.ehRepresentanteComercial; + } + + get percentualDesconto(): number { + return this._percentualDesconto; + } + + get nomeComFilial(): string { + return `${this.nome} - ${this.nomeFilial}`; + } + + // ========== BUSINESS METHODS ========== + + aplicarDescontoAoValor(valorOriginal: number): number { + if (!this.podeAplicarDesconto) { + return valorOriginal; + } + return valorOriginal * (1 - this._percentualDesconto / 100); + } +} + diff --git a/src/features/profile/index.ts b/src/features/profile/index.ts index d580e7e..eaf7d14 100644 --- a/src/features/profile/index.ts +++ b/src/features/profile/index.ts @@ -1,3 +1,4 @@ // Profile feature barrel export export { default as ProfilePage } from './components/ProfilePage'; -export type { UserProfile } from './types'; +export type { UserProfileDto } from './types'; +export { Colaborador } from './domain/Colaborador'; diff --git a/src/features/profile/services/profile.service.ts b/src/features/profile/services/profile.service.ts index 57f9576..d0924aa 100644 --- a/src/features/profile/services/profile.service.ts +++ b/src/features/profile/services/profile.service.ts @@ -1,9 +1,23 @@ import { profileApi } from '../api'; -import { UserProfile } from '../types'; +import { UserProfileDto } from '../types'; +import { Colaborador } from '../domain/Colaborador'; export const profileService = { - getMe: async (): Promise => { - const response = await profileApi.get('/auth/me'); + /** + * Obtém o colaborador atualmente autenticado como entidade de domínio. + * Segue o padrão de Linguagem Ubíqua do DDD. + */ + obterColaboradorAtual: async (): Promise => { + const response = await profileApi.get('/auth/me'); + return Colaborador.criarAPartirDoDto(response.data); + }, + + /** + * Obtém os dados brutos (DTO) do colaborador autenticado. + * Usar apenas quando a conversão para entidade de domínio não for necessária. + */ + obterColaboradorAtualDto: async (): Promise => { + const response = await profileApi.get('/auth/me'); return response.data; }, }; diff --git a/src/features/profile/types.ts b/src/features/profile/types.ts index 07a5876..27e17ba 100644 --- a/src/features/profile/types.ts +++ b/src/features/profile/types.ts @@ -1,7 +1,7 @@ /** * Tipagem da resposta do User Info (Endpoint 1.3) */ -export interface UserProfile { +export interface UserProfileDto { matricula: number; userName: string; nome: string;