Saltar al contenido principal
Versión: 2.0

Módulo: Perfil Público

Scope

Tipo de organizaciónAcceso
Empresa
Agencia
Reclutador independiente

Disponible para los tres tipos de organización. Cada tipo tiene su propio proceso de validación para poder publicar.

Permisos por rol

Empresa (COMPANY)

AcciónDisponibleCondición
Ver perfil público
Editar descripción y sitio web
Subir/eliminar imagen de perfil
Subir/eliminar portada
Publicar perfilRequiere activación completa + validación fiscal

Agencia (AGENCY)

AcciónDisponibleCondición
Ver perfil público
Editar descripción y sitio web
Subir/eliminar imagen de perfil
Subir/eliminar portada
Publicar perfilRequiere activación completa + validación operativa

Reclutador independiente (RECRUITER)

AcciónDisponibleCondición
Ver perfil público
Editar descripción y sitio web
Subir/eliminar imagen de perfil
Subir/eliminar portada
Publicar perfilRequiere activación completa + validación de identidad

¿Qué es?

El Perfil Público es la página visible de una organización dentro de la plataforma Contratta. Funciona como un "escaparate" donde la empresa, agencia o reclutador muestra su información para atraer candidatos o socios.

Este módulo es accesible para los tres tipos de organización:

  • Empresa — muestra su perfil corporativo para atraer talento
  • Agencia — presenta sus servicios de reclutamiento a empresas y candidatos
  • Reclutador independiente — muestra su experiencia y especialización

Cada tipo tiene su propio proceso de validación para poder publicar, pero la experiencia de edición y funcionalidades son las mismas para todos.

¿Qué puede hacer el usuario?

Gestionar su imagen

  • Subir una foto de perfil (desde cámara o galería)
  • Subir una imagen de portada con recorte personalizado
  • Eliminar foto o portada
  • Ver la foto en pantalla completa

Editar su información

  • Escribir una descripción pública con formato enriquecido (negritas, cursivas, títulos, listas, enlaces, bloques de código, citas)
  • Alternar entre modo edición y vista previa del texto
  • Agregar un sitio web (con validación de URL)

Publicar/Ocultar el perfil

  • Activar o desactivar la visibilidad pública del perfil
  • Ver si cumple los requisitos mínimos para publicar (ej. activación completa, datos fiscales, etc.)
  • Si no cumple requisitos, se muestra una tarjeta con los pasos pendientes y acceso directo a completarlos

Flujo del usuario

1. Abre "Perfil público" desde el menú lateral
2. Ve su portada, avatar y estado actual
3. Si no puede publicar → ve tarjeta de pendientes → puede ir a completar activación
4. Si puede publicar → activa el switch de publicación (con confirmación)
5. Edita la descripción con el editor rich text
6. Agrega su sitio web (solo el dominio, el https:// es fijo)
7. Presiona "Guardar" → se envía al servidor
8. Recibe confirmación visual de éxito o error localizado

Reglas de negocio

  • El perfil solo se puede publicar si canPublish == true (determinado por el backend según el tipo de organización y su estado de validación)
  • La descripción se almacena como HTML en el backend
  • El sitio web siempre se guarda con prefijo https://
  • Al togglear publicación se pide confirmación antes de ejecutar
  • Al eliminar imágenes se pide confirmación antes de ejecutar
  • Los errores del backend se interceptan y se muestran en el idioma activo (es/en)

Tipos de organización soportados

TipoEndpoint de validación
RECRUITER/recruiters/validation-status
AGENCY/agencies/validation-status
COMPANY/companies/validation-status

El módulo adapta automáticamente el endpoint según el tipo de organización del usuario logueado.


Arquitectura

Sigue Clean Architecture con estructura feature-first:

lib/features/profile/
├── application/ # Cubit + State
├── data/
│ ├── datasources/ # Llamadas HTTP (Dio)
│ ├── models/ # Serialización JSON
│ └── repositories/ # Implementación del contrato
├── domain/
│ ├── entities/ # Entidades inmutables
│ ├── repositories/ # Contrato abstracto
│ └── usecases/ # Operaciones de negocio
└── presentation/
├── pages/ # Página principal
└── widgets/ # Componentes UI reutilizables

Flujo de datos

UI (Page) → PublicProfileCubit → UseCase → PublicProfileRepository → RemoteDatasource → API

Los errores se propagan como Failure tipados desde el datasource hasta la UI, donde se muestran con AppNotification.

Entidades

PublicProfile

CampoTipoDescripción
idStringID del perfil
displayNameStringNombre visible de la organización
publicProfileEnabledboolSi el perfil está publicado
canPublishboolSi la organización cumple requisitos para publicar
slugString?Slug único para la URL pública
descriptionString?Descripción en HTML (rich text)
websiteUrlString?URL del sitio web
imageUrlString?URL de la imagen de perfil
coverImageUrlString?URL de la imagen de portada

ValidationStatus

CampoTipoDescripción
thresholdsMap<String, bool>Umbrales de validación cumplidos
missingRequirementsList<MissingRequirement>Requisitos faltantes con severidad

UseCases

UseCaseParámetrosRetornoDescripción
GetPublicProfileUseCase-PublicProfileObtiene el perfil actual
UpdatePublicProfileUseCasepublicProfileEnabled, description?, websiteUrl?PublicProfileActualiza descripción y sitio web
TogglePublishUseCasebool enabledPublicProfilePublica/despublica el perfil
UploadProfileImageUseCaseFile, filenamevoidSube imagen de perfil
DeleteProfileImageUseCase-voidElimina imagen de perfil
UploadCoverImageUseCaseFile, filenamevoidSube imagen de portada
DeleteCoverImageUseCase-voidElimina imagen de portada

Estado (PublicProfileState)

enum PublicProfileStatus { initial, loading, success, error }

class PublicProfileState {
final PublicProfileStatus status;
final PublicProfile? profile;
final ValidationStatus? validationStatus;
final Failure? failure;
final bool isSaving;
final bool isUploadingImage;
final bool isUploadingCover;
}

Endpoints API

MétodoEndpointDescripción
GET/public-profiles/meObtener perfil
PATCH/public-profiles/meActualizar perfil
POST/public-profiles/me/imageSubir imagen (multipart)
DELETE/public-profiles/me/imageEliminar imagen
POST/public-profiles/me/coverSubir portada (multipart)
DELETE/public-profiles/me/coverEliminar portada
GET/recruiters/validation-statusEstado de validación (reclutador)
GET/agencies/validation-statusEstado de validación (agencia)
GET/companies/validation-statusEstado de validación (empresa)

Widgets principales

WidgetResponsabilidad
ProfileCoverAndAvatarPortada + avatar con acciones de edición
ProfileEditSectionSección con toggle de publicación, editor rich text y campo de sitio web
RichTextDescriptionEditorEditor de texto enriquecido (flutter_quill) con toolbar y preview
WebsiteFieldCampo de URL con prefijo https:// fijo y validación
ProfileInfoBannerBanner con info del slug/URL pública
ProfilePendingCardCard que muestra requisitos pendientes para publicar
ProfileSaveButtonBotón de guardar con estado de loading
ProfileAvatarOptionsSheetBottom sheet con opciones de imagen (cámara, galería, eliminar)
CoverCropScreenPantalla de recorte de portada
PublicProfileSkeletonSkeleton loader durante la carga

Dependencias

  • flutter_quill — Editor de texto enriquecido
  • vsc_quill_delta_to_html — Conversión Delta → HTML
  • flutter_quill_delta_from_html — Conversión HTML → Delta (transitiva)
  • image_picker — Selección de imágenes

Registro DI (get_it)

  • Datasource: LazySingleton
  • Repository: LazySingleton
  • UseCases: LazySingleton
  • Cubit: FactoryParam<String> (recibe orgType como parámetro)
getIt<PublicProfileCubit>(param1: orgType)

Manejo de errores

  1. Datasource — deja propagar DioException sin catch
  2. Repository — captura DioException y AppException, mapea a Failure tipados via NetworkErrorHandler
  3. Cubit — emite failure en el estado
  4. UIBlocConsumer.listener muestra AppNotification.error con el mensaje localizado

Validación local de URL

El campo de sitio web valida localmente antes de enviar al backend. Si el backend devuelve un error de validación de URL, se intercepta y se muestra el mensaje localizado en vez del mensaje raw del servidor.

Localización

Todas las cadenas visibles usan context.l10n.keyName. Las claves relevantes están en lib/l10n/app_es.arb y lib/l10n/app_en.arb con prefijo profile*.