feat: Mejoras en formulario hero, estilos globales y OneSignal
- Formulario hero: validación por campo con estado touched/submitted, mensajes de error inline, máscara de moneda en cuota, autocomplete flotante para dirección y categorías con clearInput, chips para categorías y palabras clave, campos banco/CLABE/RFC - Estilos globales: border-radius y sombra en ion-button, borde para botones light sobre fondo claro, sin borde en toolbars de color - OneSignal: reescritura del servicio con login/logout, addTag y routing por título de notificación usando NgZone - FAQ: color primary en acordeón activo/desplegado Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,88 +9,124 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content padding>
|
||||
<ion-item>
|
||||
<ion-content class="ion-padding">
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['name']) && !name" [class.ion-touched]="submitted || touched['name']">
|
||||
<ion-label position="floating">{{'hero.name' | translate}}</ion-label>
|
||||
<ion-input [(ngModel)]="name"></ion-input>
|
||||
<ion-input [(ngModel)]="name" (ionBlur)="markTouched('name')"></ion-input>
|
||||
</ion-item>
|
||||
<br>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['name']) && !name">{{'hero.required' | translate}}</span>
|
||||
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['rfc']) && !rfc" [class.ion-touched]="submitted || touched['rfc']">
|
||||
<ion-label position="floating">RFC</ion-label>
|
||||
<ion-input [(ngModel)]="rfc" (ionBlur)="markTouched('rfc')"></ion-input>
|
||||
</ion-item>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['rfc']) && !rfc">{{'hero.required' | translate}}</span>
|
||||
|
||||
<!-- CATEGORÍAS CON CHIPS -->
|
||||
<ion-label class="chip-label">{{'hero.categories' | translate}}</ion-label>
|
||||
<div class="chips-container">
|
||||
<ion-chip *ngFor="let cat of categories_input" color="primary">
|
||||
<ion-label>{{cat}}</ion-label>
|
||||
<ion-icon name="close-circle" (click)="removeCategory(cat)"></ion-icon>
|
||||
</ion-chip>
|
||||
</div>
|
||||
<ion-item>
|
||||
<ion-input
|
||||
[(ngModel)]="categorySearchText"
|
||||
(ionInput)="filterCategories($event)"
|
||||
(ionFocus)="showCategoryList()"
|
||||
(ionBlur)="hideCategoryList()"
|
||||
placeholder="{{'hero.categories_placeholder' | translate}}">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
<ion-list *ngIf="showCategoryDropdown && filteredCategories.length > 0" class="dropdown-list">
|
||||
<ion-item *ngFor="let category of filteredCategories" button (click)="selectCategory(category)">
|
||||
{{category}}
|
||||
<div class="dropdown-container">
|
||||
<ion-item class="tags-item" [class.ion-invalid]="(submitted || touched['categories']) && categories_input.length === 0" [class.ion-touched]="submitted || touched['categories']">
|
||||
<div class="tags-row">
|
||||
<span class="chip-section-label">{{'hero.categories' | translate}}</span>
|
||||
<ion-chip *ngFor="let cat of categories_input" color="primary">
|
||||
<ion-label>{{cat}}</ion-label>
|
||||
<ion-icon name="close-circle" (click)="removeCategory(cat)"></ion-icon>
|
||||
</ion-chip>
|
||||
<ion-input
|
||||
[(ngModel)]="categorySearchText"
|
||||
(ionInput)="filterCategories($event)"
|
||||
(ionBlur)="hideCategoryList()"
|
||||
[clearInput]="true"
|
||||
placeholder="{{'hero.categories_placeholder' | translate}}">
|
||||
</ion-input>
|
||||
</div>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<br>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['categories']) && categories_input.length === 0">Selecciona al menos una categoría</span>
|
||||
<ion-list *ngIf="showCategoryDropdown && filteredCategories.length > 0" class="dropdown-list">
|
||||
<ion-item *ngFor="let category of filteredCategories" button (click)="selectCategory(category)">
|
||||
{{category}}
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</div>
|
||||
|
||||
<!-- PALABRAS CLAVE CON CHIPS -->
|
||||
<ion-label class="chip-label">{{'hero.keywords' | translate}}</ion-label>
|
||||
<div class="chips-container">
|
||||
<ion-chip *ngFor="let keyword of keywords" color="secondary">
|
||||
<ion-label>{{keyword}}</ion-label>
|
||||
<ion-icon name="close-circle" (click)="removeKeyword(keyword)"></ion-icon>
|
||||
</ion-chip>
|
||||
</div>
|
||||
<ion-item>
|
||||
<ion-input
|
||||
[(ngModel)]="keywordInput"
|
||||
(keydown)="onKeywordKeydown($event)"
|
||||
(ionBlur)="addKeyword($event)"
|
||||
placeholder="{{'hero.keywords_placeholder' | translate}}">
|
||||
</ion-input>
|
||||
<ion-item class="tags-item">
|
||||
<div class="tags-row">
|
||||
<span class="chip-section-label">{{'hero.keywords' | translate}}</span>
|
||||
<ion-chip *ngFor="let keyword of keywords" color="primary">
|
||||
<ion-label>{{keyword}}</ion-label>
|
||||
<ion-icon name="close-circle" (click)="removeKeyword(keyword)"></ion-icon>
|
||||
</ion-chip>
|
||||
<ion-input
|
||||
[(ngModel)]="keywordInput"
|
||||
(keydown)="onKeywordKeydown($event)"
|
||||
(ionBlur)="addKeyword($event)"
|
||||
placeholder="{{'hero.keywords_placeholder' | translate}}">
|
||||
</ion-input>
|
||||
</div>
|
||||
</ion-item>
|
||||
<ion-note padding>{{'hero.keywords_hint' | translate}}</ion-note>
|
||||
<ion-note class="ion-padding">{{'hero.keywords_hint' | translate}}</ion-note>
|
||||
|
||||
<ion-label color="light">_</ion-label>
|
||||
|
||||
<ion-item>
|
||||
<ion-label position="floating">Dirección</ion-label>
|
||||
<ion-input [(ngModel)]="addressAutocomplete" (ionChange)="autocomplete($event)" value="{{ myAddress }}" (ionFocus)="showlist()"></ion-input>
|
||||
</ion-item>
|
||||
<ion-list *ngIf="showif">
|
||||
<ion-item button=true (click)="geoloc(places.place_id, places.description, places.terms[1].value)" *ngFor="let places of placesSearch" class="place">
|
||||
{{ places.description }}
|
||||
<!-- DIRECCIÓN CON AUTOCOMPLETE FLOTANTE -->
|
||||
<div class="dropdown-container">
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['address']) && !myAddress" [class.ion-touched]="submitted || touched['address']">
|
||||
<ion-label position="floating">{{'categories.address' | translate}}</ion-label>
|
||||
<ion-input [(ngModel)]="addressAutocomplete" (ionInput)="autocomplete($event)" (ionFocus)="showlist()" (ionBlur)="blurAddressList()" [clearInput]="true"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<ion-item hidden>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['address']) && !myAddress">{{'alerts.valid_address' | translate}}</span>
|
||||
<ion-list *ngIf="showif && placesSearch?.length" class="dropdown-list">
|
||||
<ion-item button=true (click)="geoloc(places.place_id, places.description, places.terms[1].value)" *ngFor="let places of placesSearch" class="place">
|
||||
{{ places.description }}
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</div>
|
||||
|
||||
<ion-item class="ion-hide">
|
||||
<ion-label position="fixed">Latitud</ion-label>
|
||||
<ion-input value="{{ myPosition.latitude }}" disabled></ion-input>
|
||||
</ion-item>
|
||||
<ion-item hidden>
|
||||
<ion-item class="ion-hide">
|
||||
<ion-label position="fixed">Longitud</ion-label>
|
||||
<ion-input value="{{ myPosition.longitude }}" disabled></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label>{{'hero.discover' | translate}}</ion-label>
|
||||
<ion-select value={selectedReference} okText="Aceptar" cancelText="Cancelar" (ionChange)="selected_reference($event)">
|
||||
<ion-select-option value="1">{{'hero.radio' | translate}}</ion-select-option>
|
||||
<ion-select-option value="2">{{'hero.tv' | translate}}</ion-select-option>
|
||||
<ion-select-option value="3">{{'hero.social' | translate}}</ion-select-option>
|
||||
<ion-select-option value="4">{{'hero.friends' | translate}}</ion-select-option>
|
||||
<ion-select-option value="5">{{'hero.other' | translate}}</ion-select-option>
|
||||
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['bank']) && selectedBank === null" [class.ion-touched]="submitted || touched['bank']">
|
||||
<ion-label>{{'hero.bank' | translate}}</ion-label>
|
||||
<ion-select [(ngModel)]="selectedBank" placeholder="{{'hero.select' | translate}}" okText="Aceptar" cancelText="Cancelar" (ionBlur)="markTouched('bank')">
|
||||
<ion-select-option *ngFor="let bank of banks" [value]="bank.id">{{bank.name}}</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="showinput">
|
||||
<ion-label position="floating">{{'hero.discover_details' | translate}}</ion-label>
|
||||
<ion-input [(ngModel)]="reference"></ion-input>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['bank']) && selectedBank === null">{{'hero.required' | translate}}</span>
|
||||
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['bankAccount']) && !bankAccount" [class.ion-touched]="submitted || touched['bankAccount']">
|
||||
<ion-label position="floating">{{'hero.bank_account' | translate}}</ion-label>
|
||||
<ion-input [(ngModel)]="bankAccount" type="number" (ionBlur)="markTouched('bankAccount')"></ion-input>
|
||||
</ion-item>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['bankAccount']) && !bankAccount">{{'hero.required' | translate}}</span>
|
||||
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['fee']) && fee === null" [class.ion-touched]="submitted || touched['fee']">
|
||||
<ion-label position="floating">{{'hero.fee' | translate}}</ion-label>
|
||||
<ion-input [(ngModel)]="feeDisplay" (ionInput)="onFeeInput($event)" (ionBlur)="markTouched('fee')" type="text" inputmode="decimal"></ion-input>
|
||||
</ion-item>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['fee']) && fee === null">{{'hero.required' | translate}}</span>
|
||||
|
||||
<ion-item [class.ion-invalid]="(submitted || touched['reference']) && !selectedReference" [class.ion-touched]="submitted || touched['reference']">
|
||||
<ion-label>{{'hero.discover' | translate}}</ion-label>
|
||||
<ion-select [(ngModel)]="selectedReference" placeholder="{{'hero.select' | translate}}" okText="Aceptar" cancelText="Cancelar" (ionChange)="selected_reference($event)" (ionBlur)="markTouched('reference')">
|
||||
<ion-select-option [value]="1">{{'hero.radio' | translate}}</ion-select-option>
|
||||
<ion-select-option [value]="2">{{'hero.tv' | translate}}</ion-select-option>
|
||||
<ion-select-option [value]="3">{{'hero.social' | translate}}</ion-select-option>
|
||||
<ion-select-option [value]="4">{{'hero.friends' | translate}}</ion-select-option>
|
||||
<ion-select-option [value]="5">{{'hero.other' | translate}}</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['reference']) && !selectedReference">{{'hero.required' | translate}}</span>
|
||||
|
||||
<ion-item *ngIf="showinput" [class.ion-invalid]="(submitted || touched['referenceDetail']) && !reference" [class.ion-touched]="submitted || touched['referenceDetail']">
|
||||
<ion-label position="floating">{{'hero.discover_details' | translate}}</ion-label>
|
||||
<ion-input [(ngModel)]="reference" (ionBlur)="markTouched('referenceDetail')"></ion-input>
|
||||
</ion-item>
|
||||
<span class="error-msg" *ngIf="(submitted || touched['referenceDetail']) && showinput && !reference">{{'hero.required' | translate}}</span>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<ion-button type="submit" expand="full" color="secondary" (click)="addHero()">{{'hero.signup' | translate}}</ion-button>
|
||||
|
||||
Reference in New Issue
Block a user