feat: Branch betos — propiedades, rediseño postulaciones, chat contratos y flujo cancelar/repostular

- Sistema de propiedades: modal add-property con autocomplete y selector de ícono
- category.page rediseñado: selector de propiedad, fotos (Capacitor Camera), sin fecha/precio
- Modal postulate-offer: proveedor propone 2 fechas requeridas + monto con validación de cuota mínima
- viewsuppliers: chips de ordenamiento frontend (fecha, precio, certificados)
- hire.page: selección de fecha (date_1/date_2) antes de contratar; selected_date en contracts/create y contracts/coupon
- Modal contract-discussion: chat de burbujas para contratos activos
- contracted.page: botón de chat por contrato
- pending.page: botones cancelar y repostular por postulación
- Modal repostulate: pre-llena categoría/propiedad, archiva original como perdido y crea nueva con related_postulation_id
- current.page: temporizador de expiración basado en time_created (1000 min), oculta expiradas
- ichamba.service: nuevos métodos properties, cancelPostulation, contractDiscussion, updated signatures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 11:26:48 -06:00
parent 9098b126ad
commit 48979dca6f
38 changed files with 1534 additions and 617 deletions

View File

@@ -14,7 +14,7 @@
<ng-container *ngIf="loading">
<ion-card *ngFor="let _ of [1,2]">
<ion-item style="--border-color: #fff">
<ion-item class="no-separator">
<ion-label>
<ion-skeleton-text [animated]="true" style="width: 55%; height: 18px; margin-bottom: 8px"></ion-skeleton-text>
<ion-skeleton-text [animated]="true" style="width: 85%"></ion-skeleton-text>
@@ -30,15 +30,20 @@
<ng-container *ngIf="!loading">
<ng-container *ngFor="let postulation of postulations; let i = index">
<ion-card *ngIf="!postulation.already_post">
<ion-item>
<ion-card *ngIf="!postulation.already_post && postulation._timer !== 'Expirada'">
<ion-item class="no-separator">
<ion-label>
<h2 class="ion-text-capitalize">{{ postulation.category }}</h2>
<ion-text color="primary"><p class="ion-text-wrap" (click)="openMaps(postulation.lat, postulation.lng)"><b><u>{{postulation.address}}</u></b></p></ion-text>
<p class="ion-text-wrap">Referencias: {{postulation.references}}</p>
<p class="ion-text-wrap ion-text-capitalize">{{postulations_dates[i]}}</p>
<p class="ion-text-wrap">Detalles: {{postulation.details}}</p>
<p class="ion-text-wrap">Tiempo restante: {{postulation.time_limit}} minutos</p>
<p class="ion-text-wrap">
<ion-text [color]="postulation._timerCritical ? 'danger' : 'medium'">
<ion-icon name="time-outline" style="vertical-align: middle"></ion-icon>
{{ postulation._timer }}
</ion-text>
</p>
</ion-label>
<ion-button slot="end" style="height: 3em;" color="secondary" (click)="addpostulation(postulation.id)">Postularse</ion-button>
</ion-item>