diff --git a/package-lock.json b/package-lock.json index 801be69e..dd11b99e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyclos4-ui", - "version": "4.16.3", + "version": "4.16.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a870f5d6..626c3ab1 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Cyclos 4 UI", "description": "The new Cyclos 4 frontend", "icon": "cyclos.png", - "version": "4.16.3", + "version": "4.16.4", "license": "MIT", "author": { "name": "Cyclos development team", diff --git a/src/app/core/error-handler.service.ts b/src/app/core/error-handler.service.ts index 822c1f42..61521472 100644 --- a/src/app/core/error-handler.service.ts +++ b/src/app/core/error-handler.service.ts @@ -6,7 +6,7 @@ import { ErrorKind, ForbiddenError, ForbiddenErrorCode, ForgottenPasswordError, ForgottenPasswordErrorCode, InputError, InputErrorCode, NestedError, NotFoundError, PasswordStatusEnum, PaymentError, PaymentErrorCode, RedeemVoucherError, - RedeemVoucherErrorCode, ShoppingCartError, ShoppingCartErrorCode, + RedeemVoucherErrorCode, ShoppingCartCheckoutError, ShoppingCartCheckoutErrorCode, ShoppingCartError, ShoppingCartErrorCode, TopUpVoucherError, TopUpVoucherErrorCode, UnauthorizedError, UnauthorizedErrorCode, UnavailableError, UnavailableErrorCode, UserStatusEnum @@ -120,6 +120,9 @@ export class ErrorHandlerService { case ErrorKind.SHOPPING_CART: this.handleShoppingCartError(error as ShoppingCartError); return; + case ErrorKind.SHOPPING_CART_CHECKOUT: + this.handleShoppingCartCheckoutError(error as ShoppingCartCheckoutError); + return; case ErrorKind.NESTED: // An error in a nested property this.handleNestedError(error as NestedError); @@ -223,6 +226,10 @@ export class ErrorHandlerService { this.notification.error(this.shoppingCartErrorMessage(error)); } + public handleShoppingCartCheckoutError(error: ShoppingCartCheckoutError) { + this.notification.error(this.shoppingCartCheckoutErrorMessage(error)); + } + private shoppingCartErrorMessage(error: ShoppingCartError) { if (error?.code === ShoppingCartErrorCode.CAN_NOT_BUY_FROM_SELLER) { return this.i18n.ad.error.cannotBuyFromSeller; @@ -232,6 +239,15 @@ export class ErrorHandlerService { return this.general; } + private shoppingCartCheckoutErrorMessage(error: ShoppingCartCheckoutError) { + if (error?.code == ShoppingCartCheckoutErrorCode.INSUFFICIENT_BALANCE) { + return this.i18n.ad.error.insufficientBalance; + } else if (error?.code === ShoppingCartCheckoutErrorCode.PRODUCTS) { + return this.shoppingCartErrorMessage(error?.shoppingCartError); + } + return this.general; + } + public handleBuyVoucherError(error: BuyVoucherError) { if (error?.code === BuyVoucherErrorCode.PAYMENT) { this.handlePaymentError(error.paymentError); diff --git a/src/app/shared/helper.ts b/src/app/shared/helper.ts index 009af0a1..37201092 100644 --- a/src/app/shared/helper.ts +++ b/src/app/shared/helper.ts @@ -656,6 +656,13 @@ export function ensureInScroll(el: ElementReference) { } } +/** + * Returns true if the given string is not null and composed of numbers only + */ +export function isNumeric(value: string): boolean { + return value && value.length > 0 && /^[\-\+]?\d+$/.test(value); +} + /** * Returns the first words of a text, up to a maximum length. * For example: words('Social Trade Organization', 15) will return 'Social Trade'. diff --git a/src/app/ui/content/content-page.component.ts b/src/app/ui/content/content-page.component.ts index 2496f85b..634ca7dd 100644 --- a/src/app/ui/content/content-page.component.ts +++ b/src/app/ui/content/content-page.component.ts @@ -8,7 +8,7 @@ import { CardMode } from 'app/ui/content/card-mode'; import { BaseViewPageComponent } from 'app/ui/shared/base-view-page.component'; import { ActiveMenu, Menu } from 'app/ui/shared/menu'; -export const IframeResizerUrl = 'https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.2.11/iframeResizer.min.js'; +export const IframeResizerUrl = 'https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.6/iframeResizer.min.js'; /** * Displays a content page with layout diff --git a/src/app/ui/marketplace/checkout/view-cart.component.ts b/src/app/ui/marketplace/checkout/view-cart.component.ts index 617ac6d8..904d0013 100644 --- a/src/app/ui/marketplace/checkout/view-cart.component.ts +++ b/src/app/ui/marketplace/checkout/view-cart.component.ts @@ -121,7 +121,7 @@ export class ViewCartComponent * Validates and navigates to the checkout page */ protected checkout() { - if (this.unavailable || this.outOfStock || this.data.insufficientBalance) { + if (this.unavailable || this.outOfStock) { this.notification.error(this.i18n.ad.error.cannotProceedToCheckout); return; } diff --git a/src/app/ui/marketplace/view/view-order.component.ts b/src/app/ui/marketplace/view/view-order.component.ts index 110d0058..638f6092 100644 --- a/src/app/ui/marketplace/view/view-order.component.ts +++ b/src/app/ui/marketplace/view/view-order.component.ts @@ -203,7 +203,7 @@ export class ViewOrderComponent extends BaseViewPageComponent impleme const ref = this.modal.show(SetDeliveryMethodComponent, { class: 'modal-form', initialState: { name: dm.name, - chargeAmount: parseFloat(dm.price), + chargeAmount: dm.price == null ? null : parseFloat(dm.price), minTime: dm.minTime, maxTime: dm.maxTime, deliveryType: dm.deliveryType, diff --git a/src/app/ui/shared/password-hints.component.html b/src/app/ui/shared/password-hints.component.html index bf1df82d..4a218cd1 100644 --- a/src/app/ui/shared/password-hints.component.html +++ b/src/app/ui/shared/password-hints.component.html @@ -4,16 +4,24 @@ [ngClass]="(fixedLength$ | async) ? 'valid' : 'invalid'"> {{ (fixedLength$ | async) - ? i18n.password.hints.ok(i18n.password.hints.fixedLength(fixedLength)) - : i18n.password.hints.fail(i18n.password.hints.fixedLength(fixedLength)) + ? i18n.password.hints.ok(this.passwordType.onlyNumeric ? + i18n.password.hints.fixedLengthNumbers(fixedLength) : + i18n.password.hints.fixedLength(fixedLength)) + : i18n.password.hints.fail(this.passwordType.onlyNumeric ? + i18n.password.hints.fixedLengthNumbers(fixedLength) : + i18n.password.hints.fixedLength(fixedLength)) }}
  • {{ (minLength$ | async) - ? i18n.password.hints.ok(i18n.password.hints.minLength(minLength)) - : i18n.password.hints.fail(i18n.password.hints.minLength(minLength)) + ? i18n.password.hints.ok(this.passwordType.onlyNumeric ? + i18n.password.hints.minLengthNumbers(minLength) : + i18n.password.hints.minLength(minLength)) + : i18n.password.hints.fail(this.passwordType.onlyNumeric ? + i18n.password.hints.minLengthNumbers(minLength) : + i18n.password.hints.minLength(minLength)) }}
  • -
  • +
  • {{ (numbers$ | async) ? i18n.password.hints.ok(i18n.password.hints.numbers) diff --git a/src/app/ui/shared/password-hints.component.ts b/src/app/ui/shared/password-hints.component.ts index 7f7793a2..dfcb2a08 100644 --- a/src/app/ui/shared/password-hints.component.ts +++ b/src/app/ui/shared/password-hints.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Injector, Input, OnInit } from '@an import { FormControl } from '@angular/forms'; import { AvailabilityEnum, PasswordTypeDetailed } from 'app/api/models'; import { BaseComponent } from 'app/shared/base.component'; +import { isNumeric } from 'app/shared/helper'; import { BehaviorSubject } from 'rxjs'; const UpperCaseLetters = /[A-Z]/; @@ -51,8 +52,8 @@ export class PasswordHintsComponent extends BaseComponent implements OnInit { this.addSub(this.control.valueChanges.subscribe(v => { v = v ?? ''; - this.fixedLength$.next(v.length == this.fixedLength); - this.minLength$.next(v.length >= this.minLength); + this.fixedLength$.next(v.length == this.fixedLength && (!this.passwordType.onlyNumeric || isNumeric(v))); + this.minLength$.next(v.length >= this.minLength && (!this.passwordType.onlyNumeric || isNumeric(v))); this.lowerCaseLetters$.next(LowerCaseLetters.test(v)); this.upperCaseLetters$.next(UpperCaseLetters.test(v)); this.numbers$.next(Numbers.test(v)); diff --git a/src/i18n/i18n.de.json b/src/i18n/i18n.de.json index 9ae7a126..8e558286 100644 --- a/src/i18n/i18n.de.json +++ b/src/i18n/i18n.de.json @@ -1411,7 +1411,6 @@ "endDate": "Veröffentlichtes Enddatum", "error": { "cannotBuyFromSeller": "Im Moment sind Sie nicht berechtigt, Produkte von diesem Benutzer zu kaufen. Bitte kontaktieren Sie Ihren Administrator.", - "cannotProceedToCheckout": "Sie können nicht mit der Kasse fortfahren, weil einige Elemente nicht verfügbar sind oder Sie nicht genug Guthaben haben", "notEnoughStock": "Die geforderte Menge kann nicht geliefert werden, da der Artikel fast ausverkauft ist." }, "deliveryAddress": "Lieferadresse", diff --git a/src/i18n/i18n.es.json b/src/i18n/i18n.es.json index e60d14c0..90322587 100644 --- a/src/i18n/i18n.es.json +++ b/src/i18n/i18n.es.json @@ -506,7 +506,9 @@ "fail": "{text} ✗", "preface": "La contraseña debe contener:", "fixedLength": "Exactamente {length} caracteres", + "fixedLengthNumbers": "Exactamente {length} números", "minLength": "Al menos {count} caracteres", + "minLengthNumbers": "Al menos {count} números", "lowerCaseLetters": "Al menos 1 letra minúscula", "upperCaseLetters": "Al menos 1 letra mayúscula", "numbers": "Al menos 1 número", @@ -1887,9 +1889,10 @@ "endDate": "Publicado hasta", "error": { "cannotBuyFromSeller": "Por el momento, no puede comprar productos de este usuario. Por favor, contacte a su administración.", - "cannotProceedToCheckout": "No se puede continuar con la generación de la orden de compra debido a que algunos productos no están disponibles, o usted no posee saldo suficiente", + "cannotProceedToCheckout": "No puede finalizar el pedido porque algunos artículos no están disponibles", "notEnoughStock": "La cantidad solicitada no puede ser entregada porque no posee stock suficiente del el producto.", - "noAvailableAccounts": "El anuncio no se puede guardar porque no hay cuentas disponibles y el precio no se puede definir.\nPor favor, consulte a la administración." + "noAvailableAccounts": "El anuncio no se puede guardar porque no hay cuentas disponibles y el precio no se puede definir.\nPor favor, consulte a la administración.", + "insufficientBalance": "Saldo insuficiente para finalizar el pedido." }, "deliveryAddress": "Dirección de entrega", "deliveryInformation": "Información de entrega", diff --git a/src/i18n/i18n.it.json b/src/i18n/i18n.it.json index 3c627c8e..66c75657 100644 --- a/src/i18n/i18n.it.json +++ b/src/i18n/i18n.it.json @@ -995,7 +995,6 @@ "endDate": "al", "error": { "cannotBuyFromSeller": "Al momento non è consentito acquistare prodotti da questo utente. Contatta l'amministratore.", - "cannotProceedToCheckout": "Non puoi procedere con il checkout perché alcuni elementi non sono disponibili o non hai abbastanza disponibile sul conto", "notEnoughStock": "La quantità richiesta non può essere consegnata, perché l'articolo è quasi esaurito." }, "deliveryAddress": "Indirizzo di consegna", diff --git a/src/i18n/i18n.json b/src/i18n/i18n.json index e99395c7..64d14e34 100644 --- a/src/i18n/i18n.json +++ b/src/i18n/i18n.json @@ -506,7 +506,9 @@ "fail": "{text} ✗", "preface": "The password needs to contain:", "fixedLength": "Exactly {length} characters", + "fixedLengthNumbers": "Exactly {length} numbers", "minLength": "At least {count} characters", + "minLengthNumbers": "At least {count} numbers", "lowerCaseLetters": "At least 1 lower case letter", "upperCaseLetters": "At least 1 upper case letter", "numbers": "At least 1 number", @@ -1887,9 +1889,10 @@ "endDate": "Published end date", "error": { "cannotBuyFromSeller": "At the moment, you are not allowed to buy products from this user. Please contact your administrator.", - "cannotProceedToCheckout": "You can not proceed with checkout because some items are unavailable or you do not have enough balance", + "cannotProceedToCheckout": "You can not proceed with checkout because some items are unavailable", "notEnoughStock": "The quantity demanded cannot be delivered, because the article is almost out of stock.", - "noAvailableAccounts": "The advertisement can not be saved because there is not available accounts and price can not be set. Please contact the administration" + "noAvailableAccounts": "The advertisement can not be saved because there is not available accounts and price can not be set. Please contact the administration", + "insufficientBalance": "Insufficient balance to proceed with the checkout confirmation." }, "deliveryAddress": "Delivery address", "deliveryInformation": "Delivery information", diff --git a/src/i18n/i18n.nl.json b/src/i18n/i18n.nl.json index 1dc95f35..f60dafbc 100644 --- a/src/i18n/i18n.nl.json +++ b/src/i18n/i18n.nl.json @@ -1887,7 +1887,6 @@ "endDate": "Gepubliceerde einddatum", "error": { "cannotBuyFromSeller": "Op het moment, is het niet toegestaan om producten te kopen van deze gebruiker. Neem contact op met de administratie", - "cannotProceedToCheckout": "U kunt niet doorgaan met het afrekenen omdat sommige items niet beschikbaar zijn of omdat u niet genoeg saldo heeft", "notEnoughStock": "De gevraagde hoeveelheid kan niet worden geleverd, omdat het artikel bijna niet meer op voorraad is.", "noAvailableAccounts": "De advertentie kan niet worden opgeslagen omdat er geen rekeningen beschikbaar zijn en de prijs niet kan worden ingesteld.\nNeem contact op met de administratie" }, diff --git a/src/i18n/i18n.pt_BR.json b/src/i18n/i18n.pt_BR.json index 08b0d4a1..b75d4e41 100644 --- a/src/i18n/i18n.pt_BR.json +++ b/src/i18n/i18n.pt_BR.json @@ -506,7 +506,9 @@ "fail": "{text} ✗", "preface": "A senha precisa conter:", "fixedLength": "Exatamente {length} caracteres", + "fixedLengthNumbers": "Exatamente {length} números", "minLength": "Pelo menos {count} caracteres", + "minLengthNumbers": "Pelo menos {count} números", "lowerCaseLetters": "Pelo menos 1 letra minúscula", "upperCaseLetters": "Pelo menos 1 letra maiúscula", "numbers": "Pelo menos 1 número", @@ -1887,9 +1889,10 @@ "endDate": "Data final de publicação", "error": { "cannotBuyFromSeller": "No momento, você não está autorizado a comprar produtos deste usuário. Por favor, contate o seu administrador.", - "cannotProceedToCheckout": "Você não pode prosseguir com a finalização da compra, porque alguns itens não estão disponíveis ou você não tem saldo suficiente", + "cannotProceedToCheckout": "Você não pode prosseguir com a finalização da compra porque alguns itens estão indisponíveis", "notEnoughStock": "A quantidade solicitada não pode ser entregue, porque o artigo está quase sem estoque.", - "noAvailableAccounts": "O anúncio não pode ser salvo porque não há contas disponíveis e o preço não pode ser definido. Por favor, contate a administração" + "noAvailableAccounts": "O anúncio não pode ser salvo porque não há contas disponíveis e o preço não pode ser definido. Por favor, contate a administração", + "insufficientBalance": "Saldo insuficiente para prosseguir com a finalização da compra." }, "deliveryAddress": "Endereço de entrega", "deliveryInformation": "Informações de entrega", diff --git a/src/openapi/cyclos-openapi.yaml b/src/openapi/cyclos-openapi.yaml index 58ab8aa5..acf233a6 100644 --- a/src/openapi/cyclos-openapi.yaml +++ b/src/openapi/cyclos-openapi.yaml @@ -68,9 +68,9 @@ info: For details of the deprecated elements (operations and model) please visit the [deprecation notes - page](https://documentation.cyclos.org/4.16.3/api-deprecation.html) + page](https://documentation.cyclos.org/4.16.4/api-deprecation.html) for this version. - version: '4.16.3' + version: '4.16.4' servers: - url: 'http://root/api'