diff --git a/packages/admin-ui/src/lib/login/src/components/login/login.component.scss b/packages/admin-ui/src/lib/login/src/components/login/login.component.scss
index 3ef1cfa4a8..97c3ae3161 100644
--- a/packages/admin-ui/src/lib/login/src/components/login/login.component.scss
+++ b/packages/admin-ui/src/lib/login/src/components/login/login.component.scss
@@ -10,6 +10,8 @@
padding: 20px;
.login-wrapper-inner {
+
+
background: #fff;
width: 1120px;
height: 590px;
@@ -53,8 +55,8 @@
left: 0;
bottom: 0;
z-index: 10;
- background: rgb(2,0,36);
- background: linear-gradient(180deg, rgba(2,0,36,0) 0%, rgba(0,0,0,0.75) 100%);
+ background: rgb(2, 0, 36);
+ background: linear-gradient(180deg, rgba(2, 0, 36, 0) 0%, rgba(0, 0, 0, 0.75) 100%);
display: flex;
flex-direction: column;
align-items: flex-start;
@@ -74,13 +76,14 @@
.login-wrapper-image-copyright {
opacity: 0.8;
+
p {
font-size: 0.6rem;
color: white;
margin: 0 !important;
}
- a{
+ a {
color: white;
text-decoration: underline;
}
@@ -96,7 +99,7 @@
flex-direction: column;
align-items: stretch;
justify-content: center;
- box-shadow: 0px 20px 25px rgba(0,0,0,0.1);
+ box-shadow: 0px 20px 25px rgba(0, 0, 0, 0.1);
overflow: hidden;
border-radius: 5px;
flex-shrink: 0;
@@ -182,6 +185,7 @@
}
@keyframes shake {
+
10%,
90% {
transform: translate3d(-1px, 0, 0);
@@ -203,3 +207,19 @@
transform: translate3d(4px, 0, 0);
}
}
+
+.login-wrapper[dir="rtl"] {
+ .login-wrapper-inner {
+ .login-wrapper-logo {
+ right: auto;
+ left: 20px;
+ }
+
+ .login-wrapper-image {
+ .login-wrapper-image-content {
+ left: auto;
+ right: 0;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/admin-ui/src/lib/login/src/components/login/login.component.ts b/packages/admin-ui/src/lib/login/src/components/login/login.component.ts
index 6f47de67e7..e3427e984d 100644
--- a/packages/admin-ui/src/lib/login/src/components/login/login.component.ts
+++ b/packages/admin-ui/src/lib/login/src/components/login/login.component.ts
@@ -1,14 +1,16 @@
-import { HttpClient, HttpParams } from '@angular/common/http';
-import { Component } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
-import { ADMIN_UI_VERSION, AuthService, AUTH_REDIRECT_PARAM, getAppConfig } from '@vendure/admin-ui/core';
+import { ADMIN_UI_VERSION, AuthService, AUTH_REDIRECT_PARAM, getAppConfig, LocalizationDirectionType, LocalizationService } from '@vendure/admin-ui/core';
@Component({
selector: 'vdr-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
-export class LoginComponent {
+export class LoginComponent implements OnInit {
+ direction$: LocalizationDirectionType;
+
username = '';
password = '';
rememberMe = false;
@@ -24,13 +26,16 @@ export class LoginComponent {
imageCreator = '';
imageCreatorUrl = '';
- constructor(private authService: AuthService, private router: Router, private httpClient: HttpClient) {
+ constructor(private authService: AuthService, private router: Router, private httpClient: HttpClient, private localizationService: LocalizationService) {
if (this.customImageUrl) {
this.imageUrl = this.customImageUrl;
} else {
this.loadImage();
}
}
+ ngOnInit(): void {
+ this.direction$ = this.localizationService.direction$;
+ }
logIn(): void {
this.errorMessage = undefined;
diff --git a/packages/admin-ui/src/lib/static/i18n-messages/hr.json b/packages/admin-ui/src/lib/static/i18n-messages/hr.json
new file mode 100644
index 0000000000..9119278775
--- /dev/null
+++ b/packages/admin-ui/src/lib/static/i18n-messages/hr.json
@@ -0,0 +1,795 @@
+{
+ "admin": {
+ "create-new-administrator": "Kreiraj novog administratora"
+ },
+ "asset": {
+ "add-asset": "Dodaj medij",
+ "add-asset-with-count": "Dodaj {count, plural, =0 {medija} one {1 medij} few {{count} medija} other {{count} medija}}",
+ "assets-selected-count": "Odabrano {count} medija",
+ "dimensions": "Dimenzije",
+ "focal-point": "Fokalna točka",
+ "notify-create-assets-success": "Kreirano {count, plural, one {1 novo sredstvo} few {{count} nova sredstva} other {{count} novih sredstava}}",
+ "original-asset-size": "Izvorna veličina",
+ "preview": "Pregled",
+ "remove-asset": "Ukloni medij",
+ "select-asset": "Odaberi medij",
+ "select-assets": "Odaberi medije",
+ "set-as-featured-asset": "Postavi kao istaknuti medij",
+ "set-focal-point": "Postavi fokalnu točku",
+ "source-file": "Izvorna datoteka",
+ "unset-focal-point": "Poništi",
+ "update-focal-point": "Ažuriraj točku",
+ "update-focal-point-error": "Nije moguće ažurirati fokalnu točku",
+ "update-focal-point-success": "Fokalna točka ažurirana",
+ "upload-assets": "Prenesi medije",
+ "uploading": "Prenosim..."
+ },
+ "breadcrumb": {
+ "administrators": "Administratori",
+ "assets": "Mediji",
+ "channels": "Kanali",
+ "collections": "Kolekcije",
+ "countries": "Zemlje",
+ "customer-groups": "Grupa kupaca",
+ "customers": "Kupci",
+ "dashboard": "Kontrolna ploča",
+ "facets": "Aspekti",
+ "global-settings": "Globalne postavke",
+ "job-queue": "Red poslova",
+ "manage-variants": "Upravljaj varijantama",
+ "modifying": "Uređivanje",
+ "orders": "Narudžbe",
+ "payment-methods": "Načini plaćanja",
+ "product-options": "Opcije proizvoda",
+ "products": "Artikli",
+ "profile": "Profil",
+ "promotions": "Promocije",
+ "roles": "Uloge",
+ "seller-orders": "Narudžbe prodavača",
+ "sellers": "Prodavači",
+ "shipping-methods": "Načini dostave",
+ "stock-locations": "Lokacije zaliha",
+ "system-status": "Status sustava",
+ "tax-categories": "Porezne kategorije",
+ "tax-rates": "Porezne stope",
+ "zones": "Zone"
+ },
+ "catalog": {
+ "add-facet-value": "Dodaj vrijednost aspekta",
+ "add-facets": "Dodaj aspekte",
+ "add-option": "Dodaj opciju",
+ "add-price-in-another-currency": "Dodaj cijenu u drugoj valuti",
+ "add-stock-location": "Dodaj lokaciju zaliha",
+ "add-stock-to-location": "Dodaj zalihe na lokaciju",
+ "asset": "Sredstvo",
+ "asset-preview-links": "Poveznice za pregled sredstva",
+ "assets": "Sredstva",
+ "assign-product-to-channel-success": "Uspješno dodijeljeno {count, plural, one {1 proizvod} other {{count} proizvoda}} kanalu {channel}",
+ "assign-products-to-channel": "Dodijeli proizvode kanalu",
+ "assign-to-channel": "Dodijeli kanalu",
+ "assign-to-named-channel": "Dodijeli kanalu {channelCode}",
+ "assign-variant-to-channel-success": "Uspješno dodijeljeno {count, plural, one {1 varijanta proizvoda} other {{count} varijanti proizvoda}} kanalu {channel}",
+ "assign-variants-to-channel": "Dodijeli varijante proizvoda kanalu",
+ "auto-update-option-variant-name": "Automatski ažuriraj nazive varijanti proizvoda koristeći ovu opciju",
+ "auto-update-product-variant-name": "Automatski ažuriraj nazive varijanti proizvoda",
+ "cannot-create-variants-without-options": "Nije moguće stvoriti varijante proizvoda dok nije definirana grupa opcija s najmanje dvije opcije proizvoda",
+ "channel-price-preview": "Pregled cijene za kanal",
+ "collection": "Kolekcija",
+ "collection-contents": "Sadržaj kolekcije",
+ "collections": "Kolekcije",
+ "confirm-bulk-delete-products": "Izbrisati {count} proizvoda?",
+ "confirm-cancel": "Odustati?",
+ "confirm-delete-assets": "Izbrisati {count} {count, plural, one {sredstvo} other {sredstava}}?",
+ "confirm-delete-facet-value": "Izbrisati vrijednost aspekta?",
+ "confirm-delete-product": "Izbrisati proizvod?",
+ "confirm-delete-product-option": "Izbrisati opciju proizvoda \"{name}\"?",
+ "confirm-delete-product-option-group": "Izbrisati grupu opcija proizvoda \"{name}\"?",
+ "confirm-delete-product-option-group-body": "Ova grupa opcija se koristi za {count} {count, plural, one {varijantu} other {varijante}}. Jeste li sigurni da želite izbrisati?",
+ "confirm-delete-product-variant": "Izbrisati varijantu proizvoda \"{name}\"?",
+ "confirm-deletion-of-unused-variants-body": "Sljedeće varijante proizvoda su postale zastarjele zbog dodavanja novih opcija. Bit će izbrisane tijekom stvaranja novih varijanti proizvoda.",
+ "confirm-deletion-of-unused-variants-title": "",
+ "create-draft-order": "Kreiraj privremenu narudžbu",
+ "create-new-collection": "Kreiraj novu kolekciju",
+ "create-new-facet": "Kreiraj novi aspekt",
+ "create-new-product": "Novi proizvod",
+ "create-new-stock-location": "Kreiraj novu lokaciju zaliha",
+ "create-product-option-group": "Kreiraj grupu opcija proizvoda",
+ "create-product-variant": "Kreiraj varijantu proizvoda",
+ "default-currency": "Zadana valuta",
+ "do-not-inherit-filters": "Ne nasljeđuj filtre",
+ "drop-files-to-upload": "Ispustite datoteke za prijenos",
+ "edit-facet-values": "Uredi vrijednosti aspekta",
+ "edit-options": "Uredi opcije",
+ "facet": "Aspekt",
+ "facet-value-not-available": "Vrijednost aspekta \"{id}\" nije dostupna",
+ "facet-values": "Vrijednosti aspekta",
+ "facets": "Aspekti",
+ "filter-by-name": "Filtriraj po imenu",
+ "filter-by-sku": "Filtriraj po SKU",
+ "filter-inheritance": "Nasljeđivanje filtera",
+ "filters": "Filteri",
+ "inherit-filters-from-parent": "Naslijedi filtere od roditelja",
+ "live-preview-contents": "Sadržaj uživo",
+ "manage-variants": "Upravljanje varijantama",
+ "move-collection-to": "Premjesti u {name}",
+ "move-collections": "Premjesti kolekcije",
+ "move-collections-success": "Premješteno {count} kolekcija",
+ "move-down": "Premjesti dolje",
+ "move-to": "Premjesti na",
+ "move-up": "Premjesti gore",
+ "name": "Naziv",
+ "no-channel-selected": "Nije odabran kanal",
+ "no-featured-asset": "Nema istaknutih sredstava",
+ "no-selection": "Nema odabira",
+ "no-stock-locations-available-on-current-channel": "Nema dostupnih lokacija zaliha na trenutnom kanalu. Postavite barem jednu lokaciju zaliha prije dodavanja proizvoda.",
+ "notify-bulk-delete-products-success": "Uspješno izbrisano {count, plural, one {1 proizvod} other {{count} proizvoda}}",
+ "notify-remove-facets-from-channel-success": "Uspješno uklonjeno {count, plural, one {1 aspekt} other {{count} aspekta}} iz kanala {channelCode}",
+ "notify-remove-product-from-channel-error": "Nije moguće ukloniti proizvod iz kanala",
+ "notify-remove-product-from-channel-success": "Uspješno uklonjen proizvod iz kanala",
+ "notify-remove-variant-from-channel-error": "Nije moguće ukloniti varijantu proizvoda iz kanala",
+ "notify-remove-variant-from-channel-success": "Uspješno uklonjena varijanta proizvoda iz kanala",
+ "number-of-variants": "Broj varijanti",
+ "option": "Opcija",
+ "option-name": "Naziv opcije",
+ "option-values": "Vrijednosti opcije",
+ "out-of-stock-threshold": "Prag rasprodaje",
+ "out-of-stock-threshold-tooltip": "Postavlja razinu zaliha na kojoj se ova varijanta smatra rasprodanom. Upotreba negativne vrijednosti omogućuje podršku za narudžbe proizvoda koji trenutačno nisu dostupni.",
+ "page-description-options-editor": "Uredi nazive i kodove opcija za ovaj proizvod. Za dodavanje ili uklanjanje opcija koristite gumb \"Upravljanje varijantama\" ispod popisa varijanti proizvoda.",
+ "price": "Cijena",
+ "price-and-tax": "Cijena i porez",
+ "price-conversion-factor": "Faktor konverzije cijene",
+ "price-in-channel": "Cijena u { channel }",
+ "price-includes-tax-at": "Uključuje porez od { rate }%",
+ "price-with-tax-in-default-zone": "Uklj. { rate }% poreza: { price }",
+ "private": "Privatno",
+ "product": "Proizvod",
+ "product-name": "Naziv proizvoda",
+ "product-options": "Opcije proizvoda",
+ "product-variant-exists": "Varijanta proizvoda s ovim opcijama već postoji",
+ "product-variants": "Varijante proizvoda",
+ "products": "Proizvodi",
+ "public": "Javno",
+ "quick-jump-placeholder": "Brzo pretraživanje varijante",
+ "rebuild-search-index": "Obnovi indeks pretraživanja",
+ "reindex-error": "Došlo je do pogreške prilikom obnove indeksa pretraživanja",
+ "reindex-successful": "Indeksirano {count, plural, one {proizvodna varijanta} other {{count} proizvodnih varijanti}} u {time}ms",
+ "reindexing": "Obnova indeksa pretraživanja",
+ "remove-from-channel": "Ukloni iz {channelCode, select, undefined{kanala} other{{channelCode}}}",
+ "remove-option": "Ukloni opciju",
+ "remove-product-from-channel": "Ukloni proizvod iz kanala",
+ "remove-product-variant-from-channel": "Ukloni varijantu proizvoda iz kanala",
+ "reorder-collection": "Promijeni redoslijed kolekcije",
+ "root-collection": "Glavna kolekcija",
+ "run-pending-search-index-updates": "Pretraživanje: izvrši {count, plural, one {1 čekajuću nadogradnju} other {{count} čekajućih nadogradnji}}",
+ "running-search-index-updates": "Izvršava se {count, plural, one {1 nadogradnja} other {{count} nadogradnji}} indeksa pretraživanja",
+ "search-asset-name-or-tag": "Pretraži po nazivu sredstva ili oznakama",
+ "search-for-term": "Pretraži po terminu",
+ "search-product-name-or-code": "Pretraži po nazivu proizvoda ili kodu",
+ "select-product": "Odaberi proizvod",
+ "select-product-variant": "Odaberi varijantu proizvoda",
+ "sku": "SKU",
+ "slug": "Slug",
+ "slug-pattern-error": "Slug nije valjan",
+ "stock-allocated": "Dodijeljeno",
+ "stock-levels": "Razine zaliha",
+ "stock-location": "Lokacija zaliha",
+ "stock-locations": "Lokacije zaliha",
+ "stock-on-hand": "Na skladištu",
+ "tax-category": "Porezna kategorija",
+ "taxes": "Porezi",
+ "track-inventory": "Prati inventar",
+ "track-inventory-false": "Ne prati",
+ "track-inventory-inherit": "Naslijedi iz globalnih postavki",
+ "track-inventory-tooltip": "Kada se prati, razine zaliha varijanti proizvoda automatski će se prilagođavati prilikom prodaje",
+ "track-inventory-true": "Prati",
+ "update-product-option": "Ažuriraj opciju proizvoda",
+ "use-global-value": "Koristi globalnu vrijednost",
+ "values": "Vrijednosti",
+ "variant": "Varijanta",
+ "variant-count": "{count, plural, one {1 varijanta} other {{count} varijante}}",
+ "view-contents": "Pregledaj sadržaj",
+ "visibility": "Vidljivost"
+ },
+ "common": {
+ "ID": "ID",
+ "add-filter": "Dodaj filter",
+ "add-item-to-list": "Dodaj stavku na popis",
+ "add-note": "Dodaj bilješku",
+ "apply": "Primijeni",
+ "assign-to-channel": "Dodijeli kanalu",
+ "available-currencies": "Dostupne valute",
+ "available-languages": "Dostupni jezici",
+ "boolean-and": "i",
+ "boolean-false": "netočno",
+ "boolean-or": "ili",
+ "boolean-true": "točno",
+ "breadcrumb": "Trag",
+ "browser-default": "Zadano za preglednik",
+ "cancel": "Odustani",
+ "cancel-navigation": "Prekini navigaciju",
+ "change-selection": "Promijeni odabir",
+ "channel": "Kanal",
+ "channels": "Kanali",
+ "clear-selection": "Očisti odabir",
+ "code": "Kod",
+ "collapse-entries": "Skupi unose",
+ "confirm": "Potvrdi",
+ "confirm-bulk-assign-to-channel": "Dodijeliti stavke kanalu?",
+ "confirm-bulk-delete": "Izbrisati odabrane stavke?",
+ "confirm-bulk-remove-from-channel": "Ukloniti stavke iz trenutnog kanala?",
+ "confirm-delete-note": "Izbrisati bilješku?",
+ "confirm-navigation": "Potvrdi navigaciju",
+ "contents": "Sadržaj",
+ "create": "Stvori",
+ "created-at": "Stvoreno u",
+ "custom-fields": "Prilagođena polja",
+ "data-table-filter-date-mode": "Mod datuma",
+ "data-table-filter-date-range": "Raspon datuma",
+ "data-table-filter-date-relative": "Relativan datum",
+ "default-channel": "Zadani kanal",
+ "default-language": "Zadani jezik",
+ "default-tax-category": "Zadana porezna kategorija",
+ "delete": "Izbriši",
+ "description": "Opis",
+ "details": "Detalji",
+ "disabled": "Onemogućeno",
+ "discard-changes": "Odbaci promjene",
+ "edit": "Uredi",
+ "edit-field": "Uredi polje",
+ "edit-note": "Uredi bilješku",
+ "enabled": "Omogućeno",
+ "end-date": "Datum završetka",
+ "expand-entries": "Proširi unose",
+ "extension-running-in-separate-window": "Proširenje radi u zasebnom prozoru",
+ "filter": "Filter",
+ "filter-preset-name": "Filter preset name",
+ "force-delete": "Snažno izbriši",
+ "force-remove": "Snažno ukloni",
+ "general": "Općenito",
+ "guest": "Gost",
+ "id": "ID",
+ "image": "Slika",
+ "items-per-page-option": "{ count } po stranici",
+ "items-selected-count": "{ count } {count, plural, one {stavka} other {stavke}} odabrano",
+ "keep-editing": "Nastavi uređivanje",
+ "language": "Jezik",
+ "launch-extension": "Pokreni proširenje",
+ "list-items-and-n-more": "{ items } i {nMore} više",
+ "live-update": "Stvarno vrijeme ažuriranje",
+ "locale": "Lokalno",
+ "log-out": "Odjava",
+ "login": "Prijava",
+ "login-image-title": "Bok! Dobrodošli natrag. Drago nam je vidjeti vas.",
+ "login-title": "Prijavi se na {brand}",
+ "manage-tags": "Upravljanje oznakama",
+ "manage-tags-description": "Ažurirajte ili izbrišite oznake globalno.",
+ "medium-date": "Srednji datum",
+ "more": "Više...",
+ "name": "Ime",
+ "no-alerts": "Nema upozorenja",
+ "no-bulk-actions-available": "Nema dostupnih grupnih radnji",
+ "no-results": "Nema rezultata",
+ "not-applicable": "Nije primjenjivo",
+ "not-set": "Nije postavljeno",
+ "notify-assign-to-channel-success-with-count": "Uspješno dodijeljeno {count, plural, one {1 stavka} other {{count} stavki}} kanalu { channelCode }",
+ "notify-bulk-update-success": "Ažurirano { count } { entity }",
+ "notify-create-error": "Došlo je do pogreške, nije moguće stvoriti { entity }",
+ "notify-create-success": "Stvoreno novo { entity }",
+ "notify-delete-error": "Došlo je do pogreške, nije moguće izbrisati { entity }",
+ "notify-delete-error-with-count": "Nije moguće izbrisati {count, plural, one {1 stavku} other {{count} stavki}}",
+ "notify-delete-success": "Izbrisano { entity }",
+ "notify-delete-success-with-count": "Uspješno izbrisano {count, plural, one {1 stavka} other {{count} stavki}}",
+ "notify-remove-from-channel-success-with-count": "Uspješno uklonjeno { count } stavki iz kanala",
+ "notify-save-changes-error": "Došlo je do pogreške, nije moguće spremiti promjene",
+ "notify-saved-changes": "Spremljene promjene",
+ "notify-update-error": "Došlo je do pogreške, nije moguće ažurirati { entity }",
+ "notify-update-success": "Ažurirano { entity }",
+ "notify-updated-tags-success": "Uspješno ažurirane oznake",
+ "okay": "U redu",
+ "operator-contains": "sadrži",
+ "operator-eq": "jednako",
+ "operator-gt": "veće od",
+ "operator-lt": "manje od",
+ "operator-not-contains": "ne sadrži",
+ "operator-not-eq": "nije jednako",
+ "operator-notContains": "ne sadrži",
+ "operator-regex": "odgovara regexu",
+ "password": "Lozinka",
+ "position": "Pozicija",
+ "price": "Cijena",
+ "price-with-tax": "Cijena s porezom",
+ "private": "Privatno",
+ "public": "Javno",
+ "remember-me": "Zapamti me",
+ "remove": "Ukloni",
+ "remove-from-channel": "Ukloni iz trenutnog kanala",
+ "remove-item-from-list": "Ukloni stavku s popisa",
+ "rename-filter-preset": "Rename preset",
+ "reset-columns": "Ponovno postavi stupce",
+ "results-count": "{ count } {count, plural, one {rezultat} other {rezultata}}",
+ "sample-formatting": "Primjer formatiranja",
+ "save-filter-preset": "Spremi kao prečac",
+ "search-and-filter-list": "Pretraži i filtriraj ovaj popis",
+ "search-by-name": "Pretraži po imenu",
+ "select": "Odaberi...",
+ "select-display-language": "Odaberi jezik prikaza",
+ "select-items-with-count": "Odaberi { count } {count, plural, one {stavku} other {stavki}}",
+ "select-products": "Odaberi proizvode",
+ "select-relation-id": "Odaberi ID odnosa",
+ "select-table-columns": "Odaberi stupce tablice",
+ "select-today": "Odaberi danas",
+ "select-variants": "Odaberi varijante",
+ "seller": "Prodavač",
+ "set-language": "Postavi jezik",
+ "short-date": "Kratki datum",
+ "slug": "Slug",
+ "start-date": "Datum početka",
+ "status": "Status",
+ "tags": "Oznake",
+ "theme": "Tema",
+ "there-are-unsaved-changes": "Imate nespremljenih promjena. Napuštanje će rezultirati gubitkom tih promjena.",
+ "toggle-all": "Uključi/Isključi sve",
+ "total-items": "{currentStart} - {currentEnd} od {totalItems}",
+ "update": "Ažuriraj",
+ "updated-at": "Ažurirano u",
+ "username": "Korisničko ime",
+ "value": "Vrijednost",
+ "view-contents": "Pogledaj sadržaj",
+ "view-next-month": "Pogledaj sljedeći mjesec",
+ "view-previous-month": "Pogledaj prethodni mjesec",
+ "visibility": "Vidljivost",
+ "with-selected": "S odabranima ({count})..."
+ },
+ "customer": {
+ "add-customer-to-group": "Dodaj kupca u grupu",
+ "add-customer-to-groups-with-count": "Dodaj kupca u {count, plural, one {1 grupu} other {{count} grupe}}",
+ "add-customers-to-group": "Dodaj kupce u grupu",
+ "add-customers-to-group-success": "Dodano {customerCount, plural, one {1 kupac} other {{customerCount} kupaca}} u \"{ groupName }\"",
+ "add-customers-to-group-with-count": "Dodaj {count, plural, one {1 kupca} other {{count} kupaca}}",
+ "add-customers-to-group-with-name": "Dodaj kupce u \"{ groupName }\"",
+ "addresses": "Adrese",
+ "city": "Grad",
+ "company": "Tvrtka",
+ "confirm-remove-customer-from-group": "Ukloniti kupca iz grupe?",
+ "country": "Država",
+ "create-customer-group": "Stvori grupu kupaca",
+ "create-new-address": "Stvori novu adresu",
+ "create-new-customer": "Stvori novog kupca",
+ "create-new-customer-group": "Stvori novu grupu kupaca",
+ "customer": "Kupac",
+ "customer-group": "Grupa kupaca",
+ "customer-groups": "Grupe kupaca",
+ "customer-history": "Povijest kupca",
+ "customers": "Kupci",
+ "default-billing-address": "Zadana adresa za naplatu",
+ "default-shipping-address": "Zadana adresa za dostavu",
+ "email-address": "Adresa e-pošte",
+ "email-verification-sent": "Na { emailAddress } poslana je verifikacijska e-pošta",
+ "first-name": "Ime",
+ "full-name": "Puno ime",
+ "guest": "Gost",
+ "history-customer-added-to-group": "Kupac dodan u grupu \"{ groupName }\"",
+ "history-customer-address-created": "Adresa stvorena",
+ "history-customer-address-deleted": "Adresa izbrisana",
+ "history-customer-address-updated": "Adresa ažurirana",
+ "history-customer-detail-updated": "Ažurirani detalji kupca",
+ "history-customer-email-update-requested": "Zahtjev za ažuriranje adrese e-pošte",
+ "history-customer-email-update-verified": "Verifikacija ažurirane adrese e-pošte",
+ "history-customer-password-reset-requested": "Zahtjev za resetiranje lozinke",
+ "history-customer-password-reset-verified": "Verifikacija resetirane lozinke",
+ "history-customer-password-updated": "Ažurirana lozinka",
+ "history-customer-registered": "Kupac registriran",
+ "history-customer-removed-from-group": "Kupac uklonjen iz grupe \"{ groupName }\"",
+ "history-customer-verified": "Kupac verificiran",
+ "history-using-external-auth-strategy": "koristi { strategy }",
+ "history-using-native-auth-strategy": "koristi adresu e-pošte",
+ "last-login": "Posljednja prijava",
+ "last-name": "Prezime",
+ "name": "Ime",
+ "new-email-address": "Nova adresa e-pošte",
+ "no-orders-placed": "Nema postavljenih narudžbi",
+ "not-a-member-of-any-groups": "Ovaj kupac nije član nijedne grupe",
+ "old-email-address": "Stara adresa e-pošte",
+ "orders": "Narudžbe",
+ "password": "Lozinka",
+ "phone-number": "Broj telefona",
+ "postal-code": "Poštanski broj",
+ "province": "Županija",
+ "registered": "Registriran",
+ "remove-customers-from-group-success": "Uklonjeno {customerCount, plural, one {1 kupac} other {{customerCount} kupaca}} iz \"{ groupName }\"",
+ "remove-from-group": "Ukloni iz ove grupe",
+ "search-customers-by-email": "Pretraži po adresi e-pošte",
+ "search-customers-by-email-last-name-postal-code": "Pretraži po adresi e-pošte / prezimenu / poštanskom broju",
+ "select-customer": "Odaberi kupca",
+ "set-as-default-billing-address": "Postavi kao zadanu adresu za naplatu",
+ "set-as-default-shipping-address": "Postavi kao zadanu adresu za dostavu",
+ "street-line-1": "Adresa, redak 1",
+ "street-line-2": "Adresa, redak 2",
+ "title": "Naslov",
+ "update-customer-group": "Ažuriraj grupu kupaca",
+ "verified": "Verificirano",
+ "view-group-members": "Pogledaj članove grupe"
+ },
+ "dashboard": {
+ "add-widget": "Dodaj widget",
+ "latest-orders": "Najnovije narudžbe",
+ "metric-average-order-value": "Prosječna vrijednost narudžbe",
+ "metric-number-of-orders": "Broj narudžbi",
+ "metric-order-total-value": "Ukupna vrijednost narudžbe",
+ "metrics": "Statistika",
+ "orders-summary": "Sažetak narudžbi",
+ "remove-widget": "Ukloni widget",
+ "thisMonth": "Ovaj mjesec",
+ "thisWeek": "Ovaj tjedan",
+ "today": "Danas",
+ "total-order-value": "Ukupna vrijednost",
+ "total-orders": "Ukupno narudžbi",
+ "widget-resize": "Promijeni veličinu",
+ "widget-width": "Širina: {width}",
+ "yesterday": "Jučer"
+ },
+ "datetime": {
+ "ago-days": "{count, plural, one {prije 1 dan} other {prije {count} dana}}",
+ "ago-hours": "{count, plural, one {prije 1 sat} other {prije {count} sati}}",
+ "ago-minutes": "{count, plural, one {prije 1 minutu} other {prije {count} minuta}}",
+ "ago-seconds": "{count, plural, =0 {upravo sada} one {prije 1 sekunde} other {prije {count} sekundi}}",
+ "ago-years": "{count, plural, one {prije 1 godinu} other {prije {count} godina}}",
+ "day": "dan",
+ "duration-milliseconds": "{ms}ms",
+ "duration-minutes:seconds": "{m}:{s}m",
+ "duration-seconds": "{s}s",
+ "month": "mjesec",
+ "month-apr": "Travanj",
+ "month-aug": "Kolovoz",
+ "month-dec": "Prosinac",
+ "month-feb": "Veljača",
+ "month-jan": "Siječanj",
+ "month-jul": "Srpanj",
+ "month-jun": "Lipanj",
+ "month-mar": "Ožujak",
+ "month-may": "Svibanj",
+ "month-nov": "Studeni",
+ "month-oct": "Listopad",
+ "month-sep": "Rujan",
+ "relative-past-days": "Prošao {count, plural, one {1 dan} other {{count} dana}}",
+ "relative-past-months": "Prošao {count, plural, one {1 mjesec} other {{count} mjeseci}}",
+ "relative-past-years": "Prošlo {count, plural, one {1 godina} other {{count} godina}}",
+ "time": "Vrijeme",
+ "weekday-fr": "Pet",
+ "weekday-mo": "Pon",
+ "weekday-sa": "Sub",
+ "weekday-su": "Ned",
+ "weekday-th": "Čet",
+ "weekday-tu": "Uto",
+ "weekday-we": "Sri",
+ "year": "godina"
+ },
+ "editor": {
+ "image-alt": "Opis (alt)",
+ "image-src": "Izvor",
+ "image-title": "Naziv",
+ "insert-image": "Umetni sliku",
+ "link-href": "Link",
+ "link-target": "Target linka",
+ "link-title": "Naziv linka",
+ "remove-link": "Ukloni",
+ "set-link": "Postavi link"
+ },
+ "error": {
+ "403-forbidden": "Trenutno nemate ovlaštenje za pristup \"{ path }\". Ili vam nedostaju ovlasti ili je vaša sesija istekla.",
+ "could-not-connect-to-server": "Nije moguće povezati se s Vendure serverom na { url }",
+ "facet-value-form-values-do-not-match": "Broj vrijednosti u obrascu za aspekte ne podudara se s stvarnim brojem vrijednosti",
+ "health-check-failed": "Provjera zdravlja sustava nije uspjela",
+ "no-default-shipping-zone-set": "Ovaj kanal nema zadaniu zonu dostave. To može uzrokovati pogreške pri izračunavanju troškova dostave narudžbe.",
+ "no-default-tax-zone-set": "Ovaj kanal nema zadaniu poreznu zonu, što će uzrokovati pogreške pri izračunavanju cijena. Molimo kreirajte ili odaberite zonu."
+ },
+ "marketing": {
+ "actions": "Radnje",
+ "add-action": "Dodaj radnju",
+ "add-condition": "Dodaj uvjet",
+ "conditions": "Uvjeti",
+ "coupon-code": "Kod kupona",
+ "create-new-promotion": "Stvori novu promociju",
+ "ends-at": "Završava u",
+ "per-customer-limit": "Limit po kupcu",
+ "per-customer-limit-tooltip": "Maksimalni broj puta koje ovu promociju može koristiti jedan pojedinačni kupac",
+ "promotion": "Promocija",
+ "search-by-name-or-coupon-code": "Pretraži po imenu ili kodu kupona",
+ "starts-at": "Počinje u",
+ "usage-limit": "Ukupno ograničenje upotrebe",
+ "usage-limit-tooltip": "Maksimalni broj puta koje se ovu promociju može koristiti ukupno"
+ },
+ "nav": {
+ "administrators": "Administratori",
+ "assets": "Resursi",
+ "catalog": "Katalog",
+ "channels": "Kanali",
+ "collections": "Kolekcije",
+ "countries": "Države",
+ "customer-groups": "Grupe kupaca",
+ "customers": "Kupci",
+ "facets": "Aspekti",
+ "global-settings": "Globalne postavke",
+ "job-queue": "Red čekanja poslova",
+ "marketing": "Oglašavanje",
+ "orders": "Narudžbe",
+ "payment-methods": "Načini plaćanja",
+ "products": "Proizvodi",
+ "promotions": "Promocije",
+ "roles": "Uloge",
+ "sales": "Prodaja",
+ "sellers": "Prodavači",
+ "settings": "Postavke",
+ "shipping-methods": "Načini dostave",
+ "stock-locations": "Lokacije zaliha",
+ "system": "Sustav",
+ "system-status": "Status sustava",
+ "tax-categories": "Porezne kategorije",
+ "tax-rates": "Porezne stope",
+ "zones": "Zone"
+ },
+ "order": {
+ "add-item-to-order": "Dodaj stavku u narudžbu",
+ "add-note": "Dodaj napomenu",
+ "add-payment": "Dodaj plaćanje",
+ "add-payment-to-order": "Dodaj plaćanje narudžbi",
+ "add-payment-to-order-success": "Uspješno dodano plaćanje narudžbi",
+ "add-surcharge": "Dodaj nadoplatu",
+ "added-items": "Dodane stavke",
+ "amount": "Iznos",
+ "arrange-additional-payment": "Organiziraj dodatno plaćanje",
+ "billing-address": "Adresa za naplatu",
+ "cancel": "Otkaži",
+ "cancel-entire-order": "Otkaži cijelu narudžbu",
+ "cancel-fulfillment": "Otkaži ispunjenje",
+ "cancel-modification": "Otkaži izmjenu",
+ "cancel-order": "Otkaži narudžbu ili stavke",
+ "cancel-payment": "Otkaži plaćanje",
+ "cancel-reason-customer-request": "Zahtjev kupca",
+ "cancel-reason-not-available": "Nedostupno",
+ "cancel-selected-items": "Otkaži odabrane stavke",
+ "cancel-specified-items": "Otkaži određene stavke",
+ "cancellation-reason": "Razlog otkazivanja",
+ "cancelled-order-success": "Narudžba uspješno otkazana",
+ "complete-draft-order": "Završi skicu",
+ "confirm-modifications": "Potvrdi izmjene",
+ "contents": "Sadržaj",
+ "create-fulfillment": "Stvori ispunjenje",
+ "create-fulfillment-success": "Ispunjenje stvoreno",
+ "customer": "Kupac",
+ "delete-draft-order": "Izbriši skicu",
+ "draft-order": "Skica narudžbe",
+ "edit-billing-address": "Uredi adresu za naplatu",
+ "edit-shipping-address": "Uredi adresu za dostavu",
+ "error-message": "Poruka o grešci",
+ "existing-address": "Postojeća adresa",
+ "existing-customer": "Postojeći kupac",
+ "filter-is-active": "Aktivan je",
+ "fulfill": "Ispuni",
+ "fulfill-order": "Ispuni narudžbu",
+ "fulfillment": "Ispunjenje",
+ "fulfillment-method": "Metoda ispunjenja",
+ "history-coupon-code-applied": "Primijenjen kod kupona",
+ "history-coupon-code-removed": "Uklonjen kod kupona",
+ "history-fulfillment-created": "Ispunjenje stvoreno",
+ "history-fulfillment-delivered": "Ispunjenje isporučeno",
+ "history-fulfillment-shipped": "Ispunjenje poslano",
+ "history-fulfillment-transition": "Ispunjenje prešlo iz {from} u {to}",
+ "history-items-cancelled": "{count} {count, plural, one {stavka} other {stavke}} otkazane",
+ "history-order-cancelled": "Narudžba otkazana",
+ "history-order-created": "Narudžba stvorena",
+ "history-order-fulfilled": "Narudžba ispunjena",
+ "history-order-modified": "Narudžba izmijenjena",
+ "history-order-transition": "Narudžba prešla iz {from} u {to}",
+ "history-payment-settled": "Plaćanje riješeno",
+ "history-payment-transition": "Plaćanje #{id} prešlo iz {from} u {to}",
+ "history-refund-transition": "Povrat #{id} prešao iz {from} u {to}",
+ "item-count": "{count} {count, plural, one {stavka} other {stavke}}",
+ "line-fulfillment-all": "Sve stavke su ispunjene",
+ "line-fulfillment-none": "Nijedna stavka nije ispunjena",
+ "line-fulfillment-partial": "{ count } od { total } stavki ispunjeno",
+ "manually-transition-to-state": "Ručno prijeđite u stanje...",
+ "manually-transition-to-state-message": "Ručno prijeđite narudžbu u drugo stanje. Napomena da su stanja narudžbe regulirana pravilima koja mogu spriječiti određene prijelaze.",
+ "modification-adding-items": "Dodavanje {count} {count, plural, one {stavka} other {stavke}}",
+ "modification-adding-surcharges": "Dodavanje {count} {count, plural, one {nadoplata} other {nadoplata}}",
+ "modification-adjusting-lines": "Prilagođavanje {count} {count, plural, one {linija} other {linija}}",
+ "modification-not-settled": "Nije riješeno",
+ "modification-recalculate-shipping": "Ponovno izračunaj dostavu",
+ "modification-settled": "Riješeno",
+ "modification-summary": "Sažetak izmjena",
+ "modification-updating-billing-address": "Ažuriranje adrese za naplatu",
+ "modification-updating-shipping-address": "Ažuriranje adrese za dostavu",
+ "modifications": "Izmjene",
+ "modify-order": "Izmijeni narudžbu",
+ "modify-order-price-difference": "Razlika u cijeni",
+ "net-price": "Neto cijena",
+ "note": "Napomena",
+ "note-is-private": "Napomena je privatna",
+ "note-only-visible-to-administrators": "Vidljivo samo administratorima",
+ "note-visible-to-customer": "Vidljivo administratorima i kupcu",
+ "order": "Narudžba",
+ "order-history": "Povijest narudžbe",
+ "order-is-empty": "Narudžba je prazna",
+ "order-state-diagram": "Dijagram stanja narudžbe",
+ "order-type": "Vrsta narudžbe",
+ "order-type-aggregate": "Agregat",
+ "order-type-regular": "Redovita",
+ "order-type-seller": "Prodavač",
+ "orders": "Narudžbe",
+ "original-quantity-at-checkout": "Izvorna količina pri završetku kupovine",
+ "payment": "Plaćanje",
+ "payment-amount": "Iznos plaćanja",
+ "payment-metadata": "Metapodaci plaćanja",
+ "payment-method": "Metoda plaćanja",
+ "payment-state": "Stanje plaćanja",
+ "payment-to-refund": "Plaćanje za povrat",
+ "payments": "Plaćanja",
+ "placed-at": "Naručeno u",
+ "preview-changes": "Pregled promjena",
+ "product-name": "Naziv proizvoda",
+ "product-sku": "SKU",
+ "promotions-applied": "Primijenjeni promotivni kodovi",
+ "prorated-unit-price": "Proračunska cijena po komadu",
+ "quantity": "Količina",
+ "refund": "Povrat sredstava",
+ "refund-adjustment": "Prilagodba",
+ "refund-and-cancel-order": "Povrat sredstava i otkazivanje narudžbe",
+ "refund-cancellation-reason": "Razlog povrata sredstava/otkazivanja",
+ "refund-cancellation-reason-required": "Razlog povrata sredstava/otkazivanja je obavezan",
+ "refund-metadata": "Metapodaci povrata sredstava",
+ "refund-order-failed": "Povrat narudžbe nije uspio",
+ "refund-order-success": "Uspješno izvršen povrat narudžbe",
+ "refund-reason": "Razlog povrata sredstava",
+ "refund-reason-customer-request": "Zahtjev kupca",
+ "refund-reason-not-available": "Nedostupno",
+ "refund-shipping": "Povrat troškova dostave",
+ "refund-total": "Ukupan iznos povrata",
+ "refund-total-error": "Ukupan iznos povrata mora biti između {min} i {max}",
+ "refund-total-warning": "Ukupan iznos povrata premašuje odabrani iznos plaćanja. Preostali iznos povrata bit će vraćen iz drugih plaćanja.",
+ "refund-with-amount": "Povrat u iznosu od {amount}",
+ "refunded-count": "Povraćeno {count} {count, plural, one {stavka} other {stavki}}",
+ "removed-items": "Uklonjene stavke",
+ "search-by-order-filters": "Pretraga po imenu / kodu / ID transakcije",
+ "select-address": "Odaberi adresu",
+ "select-shipping-method": "Odaberi metodu dostave",
+ "select-state": "Odaberi državu",
+ "seller-orders": "Narudžbe prodavača",
+ "set-billing-address": "Postavi adresu za naplatu",
+ "set-coupon-codes": "Postavi kôdove kupona",
+ "set-customer-for-order": "Postavi kupca",
+ "set-fulfillment-state": "Označi kao {state}",
+ "set-shipping-address": "Postavi adresu za dostavu",
+ "set-shipping-method": "Postavi metodu dostave",
+ "settle-payment": "Izmiri plaćanje",
+ "settle-payment-error": "Plaćanje nije moglo biti izmireno",
+ "settle-payment-success": "Plaćanje je uspješno izmireno",
+ "settle-refund": "Izmiri povrat sredstava",
+ "settle-refund-manual-instructions": "Nakon ručnog povrata putem vašeg pružatelja plaćanja ({method}), unesite ID transakcije ovdje.",
+ "settle-refund-success": "Povrat sredstava je uspješno izmiren",
+ "shipping": "Dostava",
+ "shipping-address": "Adresa dostave",
+ "shipping-cancelled": "Dostava otkazana",
+ "shipping-method": "Metoda dostave",
+ "state": "Stanje",
+ "sub-total": "Prije poreza",
+ "successfully-updated-fulfillment": "Dostava je uspješno ažurirana",
+ "surcharges": "Dodatne naknade",
+ "tax-base": "Osnova za porez",
+ "tax-description": "Opis poreza",
+ "tax-rate": "Porezna stopa",
+ "tax-summary": "Porezni sažetak",
+ "tax-total": "Ukupan porez",
+ "total": "Ukupno",
+ "tracking-code": "Kôd za praćenje",
+ "transaction-id": "ID transakcije",
+ "transition-to-state": "Prijelaz u stanje {state}",
+ "transitioned-payment-to-state-success": "Uspješno izvršen prijelaz plaćanja u stanje {state}",
+ "transitioned-to-state-success": "Uspješno izvršen prijelaz u stanje {state}",
+ "unable-to-transition-to-state-try-another": "Narudžba se nije mogla vratiti u stanje \"{state}\". Molimo odaberite alternativno stanje",
+ "unfulfilled": "Neispunjeno",
+ "unit-price": "Jedinična cijena"
+ },
+ "settings": {
+ "add-countries-to-zone": "Dodaj države u { zoneName }",
+ "add-countries-to-zone-success": "Dodane su { countryCount } {countryCount, plural, one {država} other {države}} u zonu \"{ zoneName }\"",
+ "add-products-to-test-order": "Dodajte proizvode u test narudžbu",
+ "administrator": "Administrator",
+ "channel": "Kanal",
+ "channel-token": "Token kanala",
+ "country": "Država",
+ "create-new-channel": "Kreirajte novi kanal",
+ "create-new-country": "Kreirajte novu državu",
+ "create-new-payment-method": "Kreirajte novu metodu plaćanja",
+ "create-new-role": "Kreirajte novu ulogu",
+ "create-new-seller": "Kreirajte novog prodavača",
+ "create-new-shipping-method": "Kreirajte novu metodu dostave",
+ "create-new-tax-category": "Kreirajte novu kategoriju poreza",
+ "create-new-tax-rate": "Kreirajte novu stopu poreza",
+ "create-new-zone": "Kreirajte novu zonu",
+ "default-currency": "Zadana valuta",
+ "default-role-label": "Ovo je zadana uloga i ne može se mijenjati",
+ "default-shipping-zone": "Zadana zona dostave",
+ "default-tax-zone": "Zadana porezna zona",
+ "defaults": "Zadano",
+ "eligible": "Opravdan",
+ "email-address": "Adresa e-pošte",
+ "email-address-or-identifier": "Adresa e-pošte ili identifikator",
+ "first-name": "Ime",
+ "fulfillment-handler": "Izvršitelj",
+ "global-available-languages-tooltip": "Postavlja jezike koji su dostupni za sve kanale. Individualni kanali mogu podržavati podskup ovih jezika.",
+ "global-out-of-stock-threshold": "Globalni prag isteka zaliha",
+ "global-out-of-stock-threshold-tooltip": "Postavlja razinu zaliha na kojoj se varijanta smatra nedostupnom. Upotreba negativne vrijednosti omogućuje podršku za narudžbe izvan zaliha. Može se zamijeniti varijantama proizvoda.",
+ "last-name": "Prezime",
+ "no-eligible-shipping-methods": "Nema prikladnih metoda dostave",
+ "password": "Lozinka",
+ "payment-eligibility-checker": "Provjeravač opravdanosti plaćanja",
+ "payment-handler": "Rukovatelj plaćanjem",
+ "payment-method": "Metoda plaćanja",
+ "permissions": "Dozvole",
+ "prices-include-tax": "Cijene uključuju porez za zadanu zonu",
+ "profile": "Profil",
+ "rate": "Ocijeni",
+ "remove-countries-from-zone-success": "Uklonjeno { countryCount } {countryCount, plural, one {država} other {država}} iz zone \"{ zoneName }\"",
+ "remove-from-zone": "Uklonite iz zone",
+ "role": "Uloga",
+ "roles": "Uloge",
+ "search-by-product-name-or-sku": "Pretraži po nazivu proizvoda ili SKU",
+ "seller": "Prodavač",
+ "shipping-calculator": "Kalkulator dostave",
+ "shipping-eligibility-checker": "Provjera mogućnosti dostave",
+ "shipping-method": "Metoda dostave",
+ "tax-category": "Kategorija poreza",
+ "tax-rate": "Stopa poreza",
+ "test-address": "Testna adresa",
+ "test-result": "Rezultat testa",
+ "test-shipping-method": "Testna metoda dostave",
+ "test-shipping-methods": "Testne metode dostave",
+ "track-inventory-default": "Pratite zadani inventar",
+ "view-zone-members": "Pregledajte članove",
+ "zone": "Zona"
+ },
+ "state": {
+ "adding-items": "Dodavanje stavki",
+ "arranging-additional-payment": "Organizacija dodatnog plaćanja",
+ "arranging-payment": "Organizacija plaćanja",
+ "authorized": "Odobreno",
+ "cancelled": "Otkazano",
+ "created": "Kreirano",
+ "declined": "Odbijeno",
+ "delivered": "Isporučeno",
+ "draft": "Skica",
+ "error": "Greška",
+ "failed": "Neuspelo",
+ "modifying": "Izmene",
+ "partially-delivered": "Delimično isporučeno",
+ "partially-shipped": "Delimično otpremljeno",
+ "payment-authorized": "Odobreno plaćanje",
+ "payment-settled": "Plaćanje izmireno",
+ "pending": "U tijeku",
+ "settled": "Izmireno",
+ "shipped": "Otpremljeno"
+ },
+ "system": {
+ "all-job-queues": "Sve redove zadataka",
+ "health-all-systems-up": "Svi sistemi su dostupni",
+ "health-error": "Greška: jedan ili više sistema je nedostupno!",
+ "health-last-checked": "Poslednja provera",
+ "health-message": "Poruka",
+ "health-refresh": "Osvježi",
+ "health-status": "Status",
+ "health-status-down": "Nedostupno",
+ "health-status-up": "Dostupno",
+ "job-data": "Podaci o zadatku",
+ "job-duration": "Trajanje",
+ "job-error": "Greška zadatka",
+ "job-queue-name": "Ime reda zadatka",
+ "job-result": "Rezultat zadatka",
+ "job-state": "Stanje zadatka",
+ "job-state-all": "Svi statusi",
+ "job-state-cancelled": "Otkazano",
+ "job-state-completed": "Završeno",
+ "job-state-failed": "Neuspjelo",
+ "job-state-pending": "Na čekanju",
+ "job-state-running": "U tijeku"
+ }
+}
\ No newline at end of file
diff --git a/packages/admin-ui/src/lib/static/i18n-messages/it.json b/packages/admin-ui/src/lib/static/i18n-messages/it.json
index 7608f60e5e..46e3fc4f0d 100644
--- a/packages/admin-ui/src/lib/static/i18n-messages/it.json
+++ b/packages/admin-ui/src/lib/static/i18n-messages/it.json
@@ -45,10 +45,10 @@
"profile": "Profilo",
"promotions": "Promozioni",
"roles": "Ruoli",
- "seller-orders": "Ordini venditore",
- "sellers": "Venditore",
+ "seller-orders": "Ordini venditori",
+ "sellers": "Venditori",
"shipping-methods": "Metodi di spedizione",
- "stock-locations": "Posizioni magazzino",
+ "stock-locations": "Posizioni scorte",
"system-status": "Stato del sistema",
"tax-categories": "Impostazioni Tasse",
"tax-rates": "Importo Tasse",
@@ -59,7 +59,7 @@
"add-facets": "Aggiungi attributi",
"add-option": "Aggiungi opzione",
"add-price-in-another-currency": "Aggiungi un prezzo in un'altra valuta",
- "add-stock-location": "Aggiungi posizione magazzino",
+ "add-stock-location": "Aggiungi posizione scorte",
"add-stock-to-location": "ggiungi merce alla posizione",
"asset": "Media",
"asset-preview-links": "Media preview link",
@@ -92,8 +92,8 @@
"create-new-collection": "Crea nuova Collezione",
"create-new-facet": "Crea nuovo attributo",
"create-new-product": "Crea nuovo prodotto",
- "create-new-stock-location": "Creare una nuova posizione di magazzino",
- "create-product-option-group": "Crea gruppo di opzione di prodotto",
+ "create-new-stock-location": "Creare una nuova posizione delle scorte ",
+ "create-product-option-group": "Crea gruppo di opzione del prodotto",
"create-product-variant": "Crea variante prodotto",
"default-currency": "Valuta predefinita",
"do-not-inherit-filters": "Non ereditare i filtri",
@@ -121,7 +121,7 @@
"no-channel-selected": "Nessun canale selezionato",
"no-featured-asset": "Nessun media in evidenza",
"no-selection": "Nessuna selezione",
- "no-stock-locations-available-on-current-channel": "Non sono disponibili posizioni di magazzino nel canale corrente. Impostare almeno una posizione di magazzino prima di aggiungere prodotti.",
+ "no-stock-locations-available-on-current-channel": "Non sono disponibili posizioni delle scorte nel canale corrente. Impostare almeno una posizione delle scorte prima di aggiungere prodotti.",
"notify-bulk-delete-products-success": "Rimossi con successo {count, plural, one {1 prodotto} other {{count} prodotti}}",
"notify-remove-facets-from-channel-success": "Rimossi con successo {count, plural, one {1 attributo} other {{count} attributi}} da { channelCode }",
"notify-remove-product-from-channel-error": "Impossibile rimuovere il prodotto dal canale",
@@ -159,7 +159,7 @@
"remove-product-from-channel": "Rimuovi prodotto dal canale",
"remove-product-variant-from-channel": "Rimuovi variante dal canale",
"reorder-collection": "Riordina collezione",
- "root-collection": "",
+ "root-collection": "Collezione base",
"run-pending-search-index-updates": "Indice di ricerca: eseguire {count, plural, one {1 aggiornamento in attesa} other {{count} aggiornamenti in attesa}}",
"running-search-index-updates": "Esecuzione {count, plural, one {1 aggiornamento} other {{count} aggiornamenti}} all'indice di ricerca",
"search-asset-name-or-tag": "Cerca per nome o tag del media",
@@ -171,7 +171,7 @@
"slug": "Slug",
"slug-pattern-error": "Lo slug non è valido",
"stock-allocated": "Allocato",
- "stock-levels": "Livelli di scorte",
+ "stock-levels": "Livello delle scorte",
"stock-location": "Posizione delle scorte",
"stock-locations": "Posizioni delle scorte",
"stock-on-hand": "Scorte",
@@ -203,7 +203,7 @@
"boolean-false": "falso",
"boolean-or": "o",
"boolean-true": "vero",
- "breadcrumb": "Breadcrump",
+ "breadcrumb": "Breadcrumb",
"browser-default": "Predefinito del browser",
"cancel": "Annulla",
"cancel-navigation": "Annulla navigazione",
@@ -221,7 +221,7 @@
"confirm-navigation": "Prosegui con la navigazione",
"contents": "Contenuti",
"create": "Crea",
- "created-at": "Creato il",
+ "created-at": "Creata il",
"custom-fields": "Campi personalizzati",
"data-table-filter-date-mode": "Modalità data",
"data-table-filter-date-range": "Intervallo date",
@@ -331,7 +331,7 @@
"toggle-all": "Cambia tutti",
"total-items": "{currentStart} - {currentEnd} di {totalItems}",
"update": "Aggiorna",
- "updated-at": "Aggiornare a",
+ "updated-at": "Aggiornata il",
"username": "Username",
"value": "Valore",
"view-contents": "Mostra contenuti",
@@ -521,7 +521,7 @@
"sellers": "Venditori",
"settings": "Impostazioni",
"shipping-methods": "Metodi di Spedizione",
- "stock-locations": "Posizioni magazzino",
+ "stock-locations": "Posizioni scorte",
"system": "Sistema",
"system-status": "Stato del Sistema",
"tax-categories": "Impostazioni Tasse",
@@ -581,7 +581,7 @@
"history-order-fulfilled": "Ordine spedito",
"history-order-modified": "Ordine modificato",
"history-order-transition": "Ordine passato da {from} a {to}",
- "history-payment-settled": "Pagamento incassato",
+ "history-payment-settled": "Pagamento ricevuto",
"history-payment-transition": "Pagamento #{id} passato da {from} a {to}",
"history-refund-transition": "Rimborso #{id} passato da {from} a {to}",
"item-count": "{count} {count, plural, one {prodotto} other {prodotti}}",
@@ -593,12 +593,12 @@
"modification-adding-items": "Aggiungo {count} {count, plural, one {prodotto} other {prodotti}}",
"modification-adding-surcharges": "Aggiungo {count} {count, plural, one {sovrapprezzo} other {sovrapprezzi}}",
"modification-adjusting-lines": "Modifico {count} {count, plural, one {linea} other {linee}} dell'ordine",
- "modification-not-settled": "Non incassato",
+ "modification-not-settled": "Non ricevuto",
"modification-recalculate-shipping": "Ricalcola spedizione",
- "modification-settled": "Incassato",
+ "modification-settled": "Ricevuto",
"modification-summary": "Riepilogo delle modifiche",
- "modification-updating-billing-address": "Aggiorno indirizzo di fatturazione",
- "modification-updating-shipping-address": "Aggiorno indirizzo di spedizione",
+ "modification-updating-billing-address": "Aggiornando l'indirizzo di fatturazione",
+ "modification-updating-shipping-address": "Aggiornando l'indirizzo di spedizione",
"modifications": "Modifiche",
"modify-order": "Modifica ordine",
"modify-order-price-difference": "Differenza di prezzo",
@@ -692,7 +692,7 @@
"settings": {
"add-countries-to-zone": "Aggiungi nazioni a { zoneName }",
"add-countries-to-zone-success": "Ho aggiunto { countryCount } {countryCount, plural, one {nazione} other {nazioni}} alla zona \"{ zoneName }\"",
- "add-products-to-test-order": "Aggiunti prodotti all'ordine di test",
+ "add-products-to-test-order": "Aggiunti prodotti all'ordine di prova",
"administrator": "Amministratore",
"channel": "Canale",
"channel-token": "Token del canale",
@@ -722,8 +722,8 @@
"last-name": "Cognome",
"no-eligible-shipping-methods": "Nessun metodo di spedizione compatibile",
"password": "Password",
- "payment-eligibility-checker": "Metodo di verifica compatibilità pagamento",
- "payment-handler": "Funzione gestione pagamento",
+ "payment-eligibility-checker": "Criterio di idoneità metodo di pagamento",
+ "payment-handler": "Gestore Pagamento",
"payment-method": "Metodo di pagamento",
"permissions": "Permessi",
"prices-include-tax": "I prezzi includono le tasse per la zona fiscale predefinita",
@@ -733,24 +733,24 @@
"remove-from-zone": "Rimuovi dalla zona",
"role": "Ruolo",
"roles": "Ruoli",
- "search-by-product-name-or-sku": "Cerca per prodotto o codice SKU",
+ "search-by-product-name-or-sku": "Cerca per nome prodotto o codice SKU",
"seller": "Venditore",
- "shipping-calculator": "Calcolo prezzo spedizioni",
- "shipping-eligibility-checker": "Criterio di selezione metodi di spedizione",
+ "shipping-calculator": "Calcolo prezzo spedizione",
+ "shipping-eligibility-checker": "Criterio di idoneità metodo di spedizione",
"shipping-method": "Metodo di spedizione",
"tax-category": "Categoria tasse",
"tax-rate": "Tasso fiscale",
- "test-address": "Indirizzo di test",
- "test-result": "Risultato test",
- "test-shipping-method": "Testa metodo di spedizione",
- "test-shipping-methods": "Testa metodi di spedizione",
+ "test-address": "Indirizzo di prova",
+ "test-result": "Risultato prova",
+ "test-shipping-method": "Prova metodo di spedizione",
+ "test-shipping-methods": "Prova metodi di spedizione",
"track-inventory-default": "Monitora inventario automaticamente",
"view-zone-members": "Vedi membri",
"zone": "Zona"
},
"state": {
"adding-items": "Aggiunta prodotti",
- "arranging-additional-payment": "Scelta pagamento ulteriore",
+ "arranging-additional-payment": "Scelta ulteriore pagamento ",
"arranging-payment": "Scelta pagamento",
"authorized": "Autorizzato",
"cancelled": "Cancellato",
@@ -770,8 +770,8 @@
"shipped": "Spedito"
},
"system": {
- "all-job-queues": "Tutte le code di operazioni",
- "health-all-systems-up": "Tutto il sistema è attivo",
+ "all-job-queues": "Tutte le operazioni",
+ "health-all-systems-up": "I sistemi sono attivi",
"health-error": "Errore: uno o più sistemi sono inattivi!",
"health-last-checked": "Ultimo controllo",
"health-message": "Messaggio",
@@ -792,4 +792,4 @@
"job-state-pending": "In attesa",
"job-state-running": "In esecuzione"
}
-}
\ No newline at end of file
+}
diff --git a/packages/admin-ui/src/lib/static/i18n-messages/ne.json b/packages/admin-ui/src/lib/static/i18n-messages/ne.json
index 0824fb6d85..c9ef969d9f 100644
--- a/packages/admin-ui/src/lib/static/i18n-messages/ne.json
+++ b/packages/admin-ui/src/lib/static/i18n-messages/ne.json
@@ -28,26 +28,26 @@
"administrators": "प्रशासकहरू",
"assets": "फाइलहरू",
"channels": "च्यानलहरू",
- "collections": "संग्रहहरू",
+ "collections": "",
"countries": "देशहरू",
"customer-groups": "ग्राहक समूहहरू",
"customers": "ग्राहकहरू",
"dashboard": "ड्यासबोर्ड",
- "facets": "पहिचानहरू",
+ "facets": "गुणस्तरहरू",
"global-settings": "वैश्विक सेटिङहरू",
"job-queue": "काम कत्ति",
"manage-variants": "विविध विकल्पहरू प्रबन्ध गर्नुहोस्",
"modifying": "परिमार्जन",
- "orders": "आदेशहरू",
+ "orders": "ओर्दरहरु",
"payment-methods": "भुक्तानी विधिहरू",
"product-options": "समान विकल्पहरू",
"products": "उत्पादनहरू",
"profile": "प्रोफाइल",
"promotions": "प्रमोशनहरू",
"roles": "भूमिकाहरू",
- "seller-orders": "बेच्ने आदेशहरू",
- "sellers": "बेच्ने गर्ने व्यक्तिहरू",
- "shipping-methods": "माल गर्ने तरिकाहरू",
+ "seller-orders": "विक्रेता ओर्दरहरु",
+ "sellers": "विक्रेता व्यक्तिहरू",
+ "shipping-methods": "ढुवानी गर्ने तरिकाहरू",
"stock-locations": "स्टक स्थानहरू",
"system-status": "प्रणालीको स्थिति",
"tax-categories": "कर श्रेणिहरू",
@@ -64,37 +64,37 @@
"asset": "संपत्ति",
"asset-preview-links": "संपत्ति पूर्वावलोकन लिङ्कहरू",
"assets": "फाइलहरू",
- "assign-product-to-channel-success": "{count, plural, one {1 उत्पाद} other {{count} उत्पादहरू}} लाई { channel } मा सफलतापूर्वक लगाइएको",
- "assign-products-to-channel": "उत्पादहरूलाई च्यानलमा लगाउनुहोस्",
+ "assign-product-to-channel-success": "{count, plural, one {1 समान} other {{count} समानहरू}} लाई { channel } मा सफलतापूर्वक लगाइएको",
+ "assign-products-to-channel": "समानहरूलाई च्यानलमा लगाउनुहोस्",
"assign-to-channel": "च्यानलमा लगाउनुहोस्",
"assign-to-named-channel": "{ channelCode } मा लगाउनुहोस्",
- "assign-variant-to-channel-success": "{count, plural, one {1 उत्पाद विविधता} other {{count} उत्पाद विविधताहरू}} लाई { channel } मा सफलतापूर्वक लगाइएको",
- "assign-variants-to-channel": "उत्पाद विविधताहरूलाई च्यानलमा लगाउनुहोस्",
+ "assign-variant-to-channel-success": "{count, plural, one {1 समान विविधता} other {{count} समान विविधताहरू}} लाई { channel } मा सफलतापूर्वक लगाइएको",
+ "assign-variants-to-channel": "समान विविधताहरूलाई च्यानलमा लगाउनुहोस्",
"auto-update-option-variant-name": "विकल्प परिविकल्पको नाम आफ्नो आपदेट गर्नका लागि स्वचालित गर्नुहोस्",
- "auto-update-product-variant-name": "उत्पाद विविधताका नाम आफ्नो आपदेट गर्नका लागि स्वचालित गर्नुहोस्",
- "cannot-create-variants-without-options": "कम्ति दुई उत्पाद विकल्पसम्म विकल्प संग समावेश गरिएको पर्याप्त छेनपछि उत्पाद विविधता सिर्जना गर्न सकिदैन",
+ "auto-update-product-variant-name": "समान विविधताका नाम आफ्नो आपदेट गर्नका लागि स्वचालित गर्नुहोस्",
+ "cannot-create-variants-without-options": "कम्ति दुई समान विकल्पसम्म विकल्प संग समावेश गरिएको पर्याप्त छेनपछि समान विविधता सिर्जना गर्न सकिदैन",
"channel-price-preview": "च्यानल मूल्य पूर्वावलोकन",
- "collection": "संग्रह",
- "collection-contents": "संग्रह सामग्रीहरू",
- "collections": "संग्रहहरू",
- "confirm-bulk-delete-products": "{count} उत्पादहरू मेटाउनुहोस्?",
+ "collection": "",
+ "collection-contents": "",
+ "collections": "",
+ "confirm-bulk-delete-products": "{count} समानहरू मेटाउनुहोस्?",
"confirm-cancel": "रद्द गर्नुहोस्?",
"confirm-delete-assets": "{count} {count, plural, one {संपत्ति} other {संपत्तिहरू}} मेटाउनुहोस्?",
- "confirm-delete-facet-value": "मूल्य मेटाउनुहोस्?",
- "confirm-delete-product": "उत्पाद मेटाउनुहोस्?",
- "confirm-delete-product-option": "उत्पाद विकल्प \"{name}\" मेटाउनुहोस्?",
- "confirm-delete-product-option-group": "उत्पाद विकल्प समूह \"{name}\" मेटाउनुहोस्?",
+ "confirm-delete-facet-value": "गुणस्तर मूल्य मेटाउनुहोस्?",
+ "confirm-delete-product": "समान मेटाउनुहोस्?",
+ "confirm-delete-product-option": "समान विकल्प \"{name}\" मेटाउनुहोस्?",
+ "confirm-delete-product-option-group": "समान विकल्प समूह \"{name}\" मेटाउनुहोस्?",
"confirm-delete-product-option-group-body": "यस विकल्प समूहलाई {count} {count, plural, one {विविधता} other {विविधताहरू}} द्वारा प्रयोग गरिएको छ। के तपाईं यसलाई मेटाउन चाहनुहुन्छ?",
- "confirm-delete-product-variant": "उत्पाद विविधता \"{name}\" मेटाउनुहोस्?",
- "confirm-deletion-of-unused-variants-body": "नयाँ विकल्पहरू थप्दा पुरानो उत्पाद विविधताहरूमा प्रयुक्त भएको कारण पुरानो उत्पाद विविधताहरू मेटाइनेछन्।",
- "confirm-deletion-of-unused-variants-title": "अपयोगित उत्पाद विविधताहरू मेटाउनुहोस्?",
- "create-draft-order": "मस्यौदा आदेश सिर्जना गर्नुहोस्",
- "create-new-collection": "नयाँ संग्रह सिर्जना गर्नुहोस्",
+ "confirm-delete-product-variant": "समान विविधता \"{name}\" मेटाउनुहोस्?",
+ "confirm-deletion-of-unused-variants-body": "नयाँ विकल्पहरू थप्दा पुरानो समान विविधताहरूमा प्रयुक्त भएको कारण पुरानो समान विविधताहरू मेटाइनेछन्।",
+ "confirm-deletion-of-unused-variants-title": "अपयोगित समान विविधताहरू मेटाउनुहोस्?",
+ "create-draft-order": "मस्यौदा ओर्दर सिर्जना गर्नुहोस्",
+ "create-new-collection": "",
"create-new-facet": "नयाँ मूल्य सिर्जना गर्नुहोस्",
- "create-new-product": "नयाँ उत्पाद",
+ "create-new-product": "नयाँ समान",
"create-new-stock-location": "नयाँ स्टक स्थान सिर्जना गर्नुहोस्",
- "create-product-option-group": "उत्पाद विकल्प समूह सिर्जना गर्नुहोस्",
- "create-product-variant": "उत्पाद विविधता सिर्जना गर्नुहोस्",
+ "create-product-option-group": "समान विकल्प समूह सिर्जना गर्नुहोस्",
+ "create-product-variant": "समान विविधता सिर्जना गर्नुहोस्",
"default-currency": "पूर्वनिर्धारित मुद्रा",
"do-not-inherit-filters": "फिल्टरहरू समावेश गर्न नगर्नुहोस्",
"drop-files-to-upload": "अपलोड गर्नका लागि फाइलहरू टाँच्नुहोस्",
@@ -111,9 +111,9 @@
"inherit-filters-from-parent": "उत्तराधिकारीबाट फिल्टरहरू समावेश गर्नुहोस्",
"live-preview-contents": "सामग्री लाइभ पूर्वावलोकन",
"manage-variants": "विविधताहरू प्रबन्धन गर्नुहोस्",
- "move-collection-to": "{ name } मा सार्नुहोस्",
- "move-collections": "संग्रहहरू सार्नुहोस्",
- "move-collections-success": "{count, plural, one {1 संग्रह} other {{count} संग्रहहरू}} सारियो",
+ "move-collection-to": "",
+ "move-collections": "",
+ "move-collections-success": "",
"move-down": "तल सार्नुहोस्",
"move-to": "यहाँ सार्नुहोस्",
"move-up": "माथि सार्नुहोस्",
@@ -143,7 +143,7 @@
"price-with-tax-in-default-zone": "संलग्न गर्दछ {rate}% कर: {price}",
"private": "गोप्य",
"product": "समान",
- "product-name": "समान नाम",
+ "product-name": "समानको नाम",
"product-options": "समान विकल्पहरू",
"product-variant-exists": "यस विकल्पसहितको समान वेरिएन्ट पहिले नै छ",
"product-variants": "समान वेरिएन्टहरू",
@@ -158,16 +158,16 @@
"remove-option": "विकल्प हटाउनुहोस्",
"remove-product-from-channel": "च्यानलबाट समान हटाउनुहोस्",
"remove-product-variant-from-channel": "च्यानलबाट समान वेरिएन्ट हटाउनुहोस्",
- "reorder-collection": "संग्रह पुनः क्रममा ल्याउनुहोस्",
- "root-collection": "मूल संग्रह",
+ "reorder-collection": "",
+ "root-collection": "",
"run-pending-search-index-updates": "खोज सूची: चलाउँदै छ {count, plural, one {1 पेन्डिङ अपडेट} other {{count} पेन्डिङ अपडेटहरू}}",
"running-search-index-updates": "चलाउँदै छ {count, plural, one {1 अपडेट} other {{count} अपडेटहरू}} खोज सूचीमा",
"search-asset-name-or-tag": "संपत्ति नाम वा ट्यागका लागि खोज्नुहोस्",
"search-for-term": "मात्रकको लागि खोज्नुहोस्",
- "search-product-name-or-code": "समान नाम वा कोडका लागि खोज्नुहोस्",
+ "search-product-name-or-code": "समानको नाम वा कोडका लागि खोज्नुहोस्",
"select-product": "समान छनोट गर्नुहोस्",
"select-product-variant": "समान वेरिएन्ट छनोट गर्नुहोस्",
- "sku": "एसक्यू",
+ "sku": "SKU",
"slug": "स्लग",
"slug-pattern-error": "स्लग अमान्य छ",
"stock-allocated": "स्टक प्रियापित",
@@ -191,7 +191,7 @@
"visibility": "दृश्यता"
},
"common": {
- "ID": "पहिचान",
+ "ID": "गुणस्तर",
"add-filter": "फिल्टर थप्नुहोस्",
"add-item-to-list": "सूचीमा वस्त्र थप्नुहोस्",
"add-note": "नोट थप्नुहोस्",
@@ -215,8 +215,8 @@
"collapse-entries": "प्रवेशहरू ढिलो गर्नुहोस्",
"confirm": "पुष्टि गर्नुहोस्",
"confirm-bulk-assign-to-channel": "केहि चयन च्यानलमा खस्यौँ गर्ने?",
- "confirm-bulk-delete": "चयन गरिएका वस्तुहरूलाई हटाउने?",
- "confirm-bulk-remove-from-channel": "चयन गरिएका वस्तुहरूलाई वर्तमान च्यानलबाट हटाउने?",
+ "confirm-bulk-delete": "चयन गरिएका समानहरूलाई हटाउने?",
+ "confirm-bulk-remove-from-channel": "चयन गरिएका समानहरूलाई वर्तमान च्यानलबाट हटाउने?",
"confirm-delete-note": "नोट हटाउने?",
"confirm-navigation": "नेभिगेसन पुष्टि गर्नुहोस्",
"contents": "सामग्री",
@@ -247,14 +247,14 @@
"force-remove": "बल पुर्वप्रक्षिप्त गर्नुहोस्",
"general": "सामान्य",
"guest": "अतिथि",
- "id": "पहिचान",
+ "id": "गुणस्तर",
"image": "प्रतिमा",
"items-per-page-option": "{ पृष्ठमा गर्ने } प्रति पृष्ठ",
- "items-selected-count": "{ परिमाण } {count, plural, one {वस्तु} other {वस्तुहरू}} चयन गरिएको",
+ "items-selected-count": "{ परिमाण } {count, plural, one {समान} other {समानहरू}} चयन गरिएको",
"keep-editing": "सम्पादन जारी राख्नुहोस्",
"language": "भाषा",
"launch-extension": "एक्सटेन्सन प्रारम्भ गर्नुहोस्",
- "list-items-and-n-more": "{ वस्तुहरू } र {nMore} थप",
+ "list-items-and-n-more": "{ समानहरू } र {nMore} थप",
"live-update": "लाइभ अपडेट",
"locale": "स्थान",
"log-out": "बाहिर जानुहोस्",
@@ -271,14 +271,14 @@
"no-results": "कुनै परिणाम छैन",
"not-applicable": "लागू छैन",
"not-set": "सेट गरिएको छैन",
- "notify-assign-to-channel-success-with-count": "{ गणना } {count, plural, one {वस्तु} other {वस्तुहरू}} { channelCode } मा सफलतापूर्वक कुनै पनि दिएको छ",
+ "notify-assign-to-channel-success-with-count": "{ गणना } {count, plural, one {समान} other {समानहरू}} { channelCode } मा सफलतापूर्वक कुनै पनि दिएको छ",
"notify-bulk-update-success": "सफलतापूर्वक { count } { entity } परिमाण",
"notify-create-error": "त्रुटि देखा पर्यो, { entity } सिर्जना गर्न असमर्थ",
"notify-create-success": "{ entity } नयाँ सिर्जना गरियो",
"notify-delete-error": "त्रुटि देखा पर्यो, { entity } मेटाउन असमर्थ",
- "notify-delete-error-with-count": "{count, plural, one {1 वस्तु} other {{count} वस्तुहरू}} मेटाउन असमर्थ",
+ "notify-delete-error-with-count": "{count, plural, one {1 समान} other {{count} समानहरू}} मेटाउन असमर्थ",
"notify-delete-success": "{ entity } मेटाइयो",
- "notify-delete-success-with-count": "{count, plural, one {1 वस्तु} other {{count} वस्तुहरू}} सफलतापूर्वक मेटाइयो",
+ "notify-delete-success-with-count": "{count, plural, one {1 समान} other {{count} समानहरू}} सफलतापूर्वक मेटाइयो",
"notify-remove-from-channel-success-with-count": "{ count } च्यानलबाट सफलतापूर्वक हटाइयो",
"notify-save-changes-error": "त्रुटि देखा पर्यो, परिवर्तनहरू संरक्षण गर्न असमर्थ",
"notify-saved-changes": "परिवर्तनहरू संरक्षित गरियो",
@@ -313,8 +313,8 @@
"search-by-name": "नाम द्वारा खोज्नुहोस्",
"select": "चयन गर्नुहोस्...",
"select-display-language": "प्रदर्शन भाषा चयन गर्नुहोस्",
- "select-items-with-count": "{ count } {count, plural, one {वस्तु} other {वस्तुहरू}} चयन गर्नुहोस्",
- "select-products": "उत्पादन चयन गर्नुहोस्",
+ "select-items-with-count": "{ count } {count, plural, one {समान} other {समानहरू}} चयन गर्नुहोस्",
+ "select-products": "समानन चयन गर्नुहोस्",
"select-relation-id": "सम्बन्ध आइडी चयन गर्नुहोस्",
"select-table-columns": "तालिका स्तंभ चयन गर्नुहोस्",
"select-today": "आज चयन गर्नुहोस्",
@@ -504,7 +504,7 @@
"assets": "फाइलहरू",
"catalog": "क्याटलग",
"channels": "च्यानलहरू",
- "collections": "संग्रहहरू",
+ "collections": "",
"countries": "देशहरू",
"customer-groups": "ग्राहक समूहहरू",
"customers": "ग्राहकहरू",
@@ -512,7 +512,7 @@
"global-settings": "वैश्विक सेटिङ्ग्स",
"job-queue": "काम कतार",
"marketing": "मार्केटिङ्ग",
- "orders": "आदेशहरू",
+ "orders": "ओर्दरहरु",
"payment-methods": "भुक्तानी गर्ने तरिकाहरू",
"products": "उत्पादनहरू",
"promotions": "प्रमोशनहरू",
@@ -520,7 +520,7 @@
"sales": "बिक्री",
"sellers": "बेच्नेहरू",
"settings": "सेटिङ्ग्स",
- "shipping-methods": "पारिश्रमिक तरिकाहरू",
+ "shipping-methods": "ढुवानी तरिकाहरू",
"stock-locations": "स्टक स्थानहरू",
"system": "प्रणाली",
"system-status": "प्रणाली स्थिति",
@@ -529,28 +529,28 @@
"zones": "क्षेत्रहरू"
},
"order": {
- "add-item-to-order": "आदेशमा वस्तु थप्नुहोस्",
+ "add-item-to-order": "ओर्दरमा समान थप्नुहोस्",
"add-note": "टिप्पणी थप्नुहोस्",
"add-payment": "भुक्तानी थप्नुहोस्",
- "add-payment-to-order": "आदेशमा भुक्तानी थप्नुहोस्",
- "add-payment-to-order-success": "आदेशमा भुक्तानी सफलतापूर्वक थपियो",
+ "add-payment-to-order": "ओर्दरमा भुक्तानी थप्नुहोस्",
+ "add-payment-to-order-success": "ओर्दरमा भुक्तानी सफलतापूर्वक थपियो",
"add-surcharge": "सर्चार्ज थप्नुहोस्",
- "added-items": "थपिएका वस्तुहरू",
+ "added-items": "थपिएका समानहरू",
"amount": "रकम",
"arrange-additional-payment": "अतिरिक्त भुक्तानी गर्नुहोस्",
"billing-address": "बिलिङ ठेगाना",
"cancel": "रद्द गर्नुहोस्",
- "cancel-entire-order": "पूरा आदेश रद्द गर्नुहोस्",
+ "cancel-entire-order": "पूरा ओर्दर रद्द गर्नुहोस्",
"cancel-fulfillment": "पूर्णाउण रद्द गर्नुहोस्",
"cancel-modification": "संशोधन रद्द गर्नुहोस्",
- "cancel-order": "आदेश वा वस्तुहरू रद्द गर्नुहोस्",
+ "cancel-order": "ओर्दर वा समानहरू रद्द गर्नुहोस्",
"cancel-payment": "भुक्तानी रद्द गर्नुहोस्",
"cancel-reason-customer-request": "ग्राहकको अनुरोध",
"cancel-reason-not-available": "उपलब्ध छैन",
- "cancel-selected-items": "चयन गरिएका वस्तुहरू रद्द गर्नुहोस्",
- "cancel-specified-items": "निर्दिष्ट वस्तुहरू रद्द गर्नुहोस्",
+ "cancel-selected-items": "चयन गरिएका समानहरू रद्द गर्नुहोस्",
+ "cancel-specified-items": "निर्दिष्ट समानहरू रद्द गर्नुहोस्",
"cancellation-reason": "रद्द गर्ने कारण",
- "cancelled-order-success": "आदेश सफलतापूर्वक रद्द गरियो",
+ "cancelled-order-success": "ओर्दर सफलतापूर्वक रद्द गरियो",
"complete-draft-order": "पूरा प्रारूप",
"confirm-modifications": "संशोधनहरू पुष्टि गर्नुहोस्",
"contents": "अनुकूलन",
@@ -558,7 +558,7 @@
"create-fulfillment-success": "पूर्णाउण सफलतापूर्वक सिर्जना गरियो",
"customer": "ग्राहक",
"delete-draft-order": "प्रारूप रद्द गर्नुहोस्",
- "draft-order": "प्रारूप आदेश",
+ "draft-order": "प्रारूप ओर्दर",
"edit-billing-address": "बिलिङ ठेगाना सम्पादन गर्नुहोस्",
"edit-shipping-address": "पारिश्रमिक ठेगाना सम्पादन गर्नुहोस्",
"error-message": "त्रुटि संदेश",
@@ -566,7 +566,7 @@
"existing-customer": "मौजूदा ग्राहक",
"filter-is-active": "सक्रिय छ",
"fulfill": "पूर्णाउनुहोस्",
- "fulfill-order": "पूर्णाउण आदेश",
+ "fulfill-order": "पूर्णाउण ओर्दर",
"fulfillment": "पूर्णाउण",
"fulfillment-method": "पूर्णाउण तरिका",
"history-coupon-code-applied": "कुपन कोड लागू गरियो",
@@ -575,22 +575,22 @@
"history-fulfillment-delivered": "पूर्णाउण पुर्याइयो",
"history-fulfillment-shipped": "पूर्णाउण पठाइयो",
"history-fulfillment-transition": "पूर्णाउण यात्रा गर्दा {from} बाट {to} मा",
- "history-items-cancelled": "{count} {count, plural, one {वस्तु} other {वस्तुहरू}} रद्द गरियो",
- "history-order-cancelled": "आदेश रद्द गरियो",
- "history-order-created": "आदेश सिर्जना गरियो",
- "history-order-fulfilled": "आदेश पूर्ण भयो",
- "history-order-modified": "आदेश संशोधन गरियो",
- "history-order-transition": "आदेशको यात्रा गर्दा {from} बाट {to} मा",
+ "history-items-cancelled": "{count} {count, plural, one {समान} other {समानहरू}} रद्द गरियो",
+ "history-order-cancelled": "ओर्दर रद्द गरियो",
+ "history-order-created": "ओर्दर सिर्जना गरियो",
+ "history-order-fulfilled": "ओर्दर पूर्ण भयो",
+ "history-order-modified": "ओर्दर संशोधन गरियो",
+ "history-order-transition": "ओर्दरको यात्रा गर्दा {from} बाट {to} मा",
"history-payment-settled": "भुक्तानी लिएको",
"history-payment-transition": "भुक्तानी #{id} लाई {from} बाट {to} मा यात्रा गरियो",
"history-refund-transition": "वापसी #{id} लाई {from} बाट {to} मा यात्रा गरियो",
- "item-count": "{count} {count, plural, one {वस्तु} other {वस्तुहरू}}",
+ "item-count": "{count} {count, plural, one {समान} other {समानहरू}}",
"line-fulfillment-all": "सबै लाएन पूर्ण भएको छ",
"line-fulfillment-none": "कुनै पनि लाएन पूर्ण भएको छैन",
"line-fulfillment-partial": "{count} मध्ये {total} लाएन पूर्ण भएको छ",
"manually-transition-to-state": "हातमा यात्रा गर्नुहोस्...",
- "manually-transition-to-state-message": "आदेश अझै अर्को स्थितिमा हातमा यात्रा गर्नुहोस्। कृपया ध्यान दिनुहोस् कि आदेश स्थितिहरूले केहि यात्रा रोक्ने नियमहरू अनुसरण गर्दछ जसले केहि यात्राहरूलाई रोक्दै गर्दछ।",
- "modification-adding-items": "{count} {count, plural, one {वस्तु} other {वस्तुहरू}} थपिएको छ",
+ "manually-transition-to-state-message": "ओर्दर अझै अर्को स्थितिमा हातमा यात्रा गर्नुहोस्। कृपया ध्यान दिनुहोस् कि ओर्दर स्थितिहरूले केहि यात्रा रोक्ने नियमहरू अनुसरण गर्दछ जसले केहि यात्राहरूलाई रोक्दै गर्दछ।",
+ "modification-adding-items": "{count} {count, plural, one {समान} other {समानहरू}} थपिएको छ",
"modification-adding-surcharges": "{count} {count, plural, one {सर्चार्ज} other {सर्चार्जहरू}} थपिएको छ",
"modification-adjusting-lines": "{count} {count, plural, one {लाइन} other {लाइनहरू}} सम्पादन गरिएको छ",
"modification-not-settled": "लिएको छैन",
@@ -600,22 +600,22 @@
"modification-updating-billing-address": "बिलिङ ठेगाना अपडेट गरिएको छ",
"modification-updating-shipping-address": "पारिश्रमिक ठेगाना अपडेट गरिएको छ",
"modifications": "संशोधनहरू",
- "modify-order": "आदेश संशोधन गर्नुहोस्",
+ "modify-order": "ओर्दर संशोधन गर्नुहोस्",
"modify-order-price-difference": "मूल्य अन्तर",
"net-price": "नेट मूल्य",
"note": "टिप्पणी",
"note-is-private": "टिप्पणी प्राइभेट छ",
"note-only-visible-to-administrators": "मात्र प्रशासकहरूले मात्र हेर्न सक्छन्",
"note-visible-to-customer": "प्रशासकहरू र ग्राहकहरूले हेर्न सक्छन्",
- "order": "आदेश",
- "order-history": "आदेश इतिहास",
- "order-is-empty": "आदेश खाली छ",
- "order-state-diagram": "आदेश स्थिति डायग्राम",
- "order-type": "आदेश प्रकार",
+ "order": "ओर्दर",
+ "order-history": "ओर्दर इतिहास",
+ "order-is-empty": "ओर्दर खाली छ",
+ "order-state-diagram": "ओर्दर स्थिति डायग्राम",
+ "order-type": "ओर्दर प्रकार",
"order-type-aggregate": "संचयन",
"order-type-regular": "साधारण",
"order-type-seller": "विक्रेता",
- "orders": "आदेशहरू",
+ "orders": "ओर्दरहरु",
"original-quantity-at-checkout": "चेकआउटमा मूल संख्या",
"payment": "भुक्तानी",
"payment-amount": "भुक्तानी रकम",
@@ -627,18 +627,18 @@
"placed-at": "मा राखिएको",
"preview-changes": "पूर्वावलोकन परिवर्तनहरू",
"product-name": "समानको नाम",
- "product-sku": "समानको एसक्यू",
+ "product-sku": "SKU",
"promotions-applied": "प्रचारहरू लागू गरिएको",
"prorated-unit-price": "प्राप्त मूल्य यूनिट मूल्य",
"quantity": "मात्रा",
"refund": "वापसी",
"refund-adjustment": "सम्यक गर्दैछ",
- "refund-and-cancel-order": "वापसी गर्दै र आदेश रद्द गर्दै",
+ "refund-and-cancel-order": "वापसी गर्दै र ओर्दर रद्द गर्दै",
"refund-cancellation-reason": "वापसी/रद्द गर्दै कारण",
"refund-cancellation-reason-required": "वापसी/रद्द गर्दै कारण आवश्यक छ",
"refund-metadata": "वापसी मेटाडाटा",
- "refund-order-failed": "आदेश वापसी गर्दा असफल भयो",
- "refund-order-success": "आदेश सफलतापूर्वक वापसी गरियो",
+ "refund-order-failed": "ओर्दर वापसी गर्दा असफल भयो",
+ "refund-order-success": "ओर्दर सफलतापूर्वक वापसी गरियो",
"refund-reason": "वापसी कारण",
"refund-reason-customer-request": "ग्राहकको अनुरोध",
"refund-reason-not-available": "उपलब्ध छैन",
@@ -647,19 +647,19 @@
"refund-total-error": "कुल वापसी {min} र {max} को बीचमा हुनुपर्छ",
"refund-total-warning": "कुल वापसी चयन गरिएको भुक्तानी रकमलाई माथि पुर्याइएको छैन। अरू भुक्तानीहरूबाट बाँकी वापसी रकम वापसी गरिनेछ।",
"refund-with-amount": "{amount} वापसी गर्नुहोस्",
- "refunded-count": "{count} {count, plural, one {वस्तु} other {वस्तुहरू}} वापसी गरियो",
- "removed-items": "हटाइएका वस्तुहरू",
+ "refunded-count": "{count} {count, plural, one {समान} other {समानहरू}} वापसी गरियो",
+ "removed-items": "हटाइएका समानहरू",
"search-by-order-filters": "नाम / कोड / लेखा निम्ति खोज्नुहोस्",
"select-address": "ठेगाना चयन गर्नुहोस्",
- "select-shipping-method": "पारिश्रमिक तरिका चयन गर्नुहोस्",
+ "select-shipping-method": "ढुवानी तरिका चयन गर्नुहोस्",
"select-state": "स्थिति चयन गर्नुहोस्",
- "seller-orders": "विक्रेता आदेशहरू",
+ "seller-orders": "विक्रेता ओर्दरहरु",
"set-billing-address": "बिलिङ ठेगाना सेट गर्नुहोस्",
"set-coupon-codes": "कुपन कोड सेट गर्नुहोस्",
"set-customer-for-order": "ग्राहक सेट गर्नुहोस्",
"set-fulfillment-state": "{state} मा चिन्ह गर्नुहोस्",
- "set-shipping-address": "पारिश्रमिक ठेगाना सेट गर्नुहोस्",
- "set-shipping-method": "पारिश्रमिक तरिका सेट गर्नुहोस्",
+ "set-shipping-address": "ढुवानी ठेगाना सेट गर्नुहोस्",
+ "set-shipping-method": "ढुवानी तरिका सेट गर्नुहोस्",
"settle-payment": "भुक्तानी गर्नुहोस्",
"settle-payment-error": "भुक्तानी सफल गर्न सकिएन",
"settle-payment-success": "भुक्तानी सफलतापूर्वक सेटल गरियो",
@@ -667,9 +667,9 @@
"settle-refund-manual-instructions": "तपाईंको भुक्तान प्रदान गर्दा मान्य भएको छ, तलका मा अर्को भुक्तान कोडहरू छन् ({method}), यहाँ व्यक्त गर्नुहोस्।",
"settle-refund-success": "वापसी सफलतापूर्वक सेटल गरियो",
"shipping": "पारिश्रमिक",
- "shipping-address": "पारिश्रमिक ठेगाना",
- "shipping-cancelled": "पारिश्रमिक रद्द गरियो",
- "shipping-method": "पारिश्रमिक तरिका",
+ "shipping-address": "ढुवानी ठेगाना",
+ "shipping-cancelled": "ढुवानी रद्द गरियो",
+ "shipping-method": "ढुवानी तरिका",
"state": "स्थिति",
"sub-total": "उपकुल",
"successfully-updated-fulfillment": "पूर्णाउण सफलतापूर्वक अपडेट गरियो",
@@ -685,14 +685,14 @@
"transition-to-state": "{ state } स्थिति मा यात्रा गर्नुहोस्",
"transitioned-payment-to-state-success": "{ state } मा भुक्तानी सफलतापूर्वक यात्रा गरियो",
"transitioned-to-state-success": "{ state } मा सफलतापूर्वक यात्रा गरियो",
- "unable-to-transition-to-state-try-another": "आदेशलाई फर्का \"{state}\" स्थितिमा यात्रा गर्न सकिएन। कृपया अन्य एक विकल्प चयन गर्नुहोस्।",
+ "unable-to-transition-to-state-try-another": "ओर्दरलाई फर्का \"{state}\" स्थितिमा यात्रा गर्न सकिएन। कृपया अन्य एक विकल्प चयन गर्नुहोस्।",
"unfulfilled": "पूर्ण नहुने",
"unit-price": "यूनिट मूल्य"
},
"settings": {
"add-countries-to-zone": "देशहरू थप्नुहोस् { zoneName } मा",
"add-countries-to-zone-success": "थपिएको छ { countryCount } {countryCount, plural, one {देश} other {देशहरू}} मा \"{ zoneName }\" जोनमा",
- "add-products-to-test-order": "परीक्षण आदेशमा समानन थप्नुहोस्",
+ "add-products-to-test-order": "परीक्षण ओर्दरमा समानन थप्नुहोस्",
"administrator": "प्रशासक",
"channel": "स्रोत",
"channel-token": "स्रोत टोकन",
@@ -702,7 +702,7 @@
"create-new-payment-method": "नयाँ भुक्तानी तरिका बनाउनुहोस्",
"create-new-role": "नयाँ भूमिका बनाउनुहोस्",
"create-new-seller": "नयाँ विक्रेता बनाउनुहोस्",
- "create-new-shipping-method": "नयाँ पारिश्रमिक तरिका बनाउनुहोस्",
+ "create-new-shipping-method": "नयाँ ढुवानी तरिका बनाउनुहोस्",
"create-new-tax-category": "कर श्रेणी बनाउनुहोस्",
"create-new-tax-rate": "नयाँ कर दर बनाउनुहोस्",
"create-new-zone": "नयाँ जोन बनाउनुहोस्",
@@ -713,14 +713,14 @@
"defaults": "पूर्वनिर्धारित",
"eligible": "योग्य",
"email-address": "ईमेल ठेगाना",
- "email-address-or-identifier": "ईमेल ठेगाना वा पहिचानकर्ता",
+ "email-address-or-identifier": "ईमेल ठेगाना वा गुणस्तरकर्ता",
"first-name": "पहिलो नाम",
"fulfillment-handler": "पूर्णाउण प्राप्त गर्ने व्यक्ति",
"global-available-languages-tooltip": "सबै स्रोतका लागि उपलब्ध गराइएको भाषाहरू सेट गर्दछ। व्यक्तिगत स्रोतहरू तपाईंको विशिष्ट भाषा समर्थन गर्दछन्।",
"global-out-of-stock-threshold": "वैशिष्ट्यिक अप्राप्य गरिएको स्टक स्तर",
"global-out-of-stock-threshold-tooltip": "यसले यस एक वैशिष्ट्यिक भरिएको स्टक स्तरमा रहेको छ जसले यस विशेष भरिएको छलैन्छ। नकारात्मक मूल्य प्रतिक्षिप्त बनाउँदछ। समान विशेष द्वारा अद्यायित गर्न सकिन्छ।",
"last-name": "थर",
- "no-eligible-shipping-methods": "कुनै पनि योग्य पारिश्रमिक तरिका छैन",
+ "no-eligible-shipping-methods": "कुनै पनि योग्य ढुवानी तरिका छैन",
"password": "पासवर्ड",
"payment-eligibility-checker": "भुक्तानी योग्यता चेकर",
"payment-handler": "भुक्तानी ह्यान्डलर",
@@ -733,23 +733,23 @@
"remove-from-zone": "जोनबाट हटाउनुहोस्",
"role": "भूमिका",
"roles": "भूमिकाहरू",
- "search-by-product-name-or-sku": "समानको नाम वा एसक्यू द्वारा खोज्नुहोस्",
+ "search-by-product-name-or-sku": "समानको नाम वा SKU द्वारा खोज्नुहोस्",
"seller": "विक्रेता",
"shipping-calculator": "पारिश्रमिक क्याल्कुलेटर",
"shipping-eligibility-checker": "पारिश्रमिक योग्यता चेकर",
- "shipping-method": "पारिश्रमिक तरिका",
+ "shipping-method": "ढुवानी तरिका",
"tax-category": "कर श्रेणी",
"tax-rate": "कर दर",
"test-address": "परीक्षण ठेगाना",
"test-result": "परीक्षण परिणाम",
- "test-shipping-method": "परीक्षण पारिश्रमिक तरिका",
- "test-shipping-methods": "परीक्षण पारिश्रमिक तरिकाहरू",
+ "test-shipping-method": "परीक्षण ढुवानी तरिका",
+ "test-shipping-methods": "परीक्षण ढुवानी तरिकाहरू",
"track-inventory-default": "पूर्वनिर्धारित रूपमा स्टक परिवर्तन गर्नुहोस्",
"view-zone-members": "सदस्यहरू हेर्नुहोस्",
"zone": "जोन"
},
"state": {
- "adding-items": "वस्तुहरू थप्दै छ",
+ "adding-items": "समानहरू थप्दै छ",
"arranging-additional-payment": "अतिरिक्त भुक्तान योजना गर्दै",
"arranging-payment": "भुक्तान योजना गर्दै",
"authorized": "स्वीकृत",
diff --git a/packages/admin-ui/src/lib/static/styles/global/_utilities.scss b/packages/admin-ui/src/lib/static/styles/global/_utilities.scss
index eb7330fdee..8c072b2530 100644
--- a/packages/admin-ui/src/lib/static/styles/global/_utilities.scss
+++ b/packages/admin-ui/src/lib/static/styles/global/_utilities.scss
@@ -52,17 +52,17 @@ $spacings: (1, 2, 3, 4, 5, auto);
$sides: (
't': 'top',
'b': 'bottom',
- 'l': 'left',
- 'r': 'right',
+ 'l': 'inline-start',
+ 'r': 'inline-end',
'': (
'top',
- 'left',
+ 'inline-start',
'bottom',
- 'right',
+ 'inline-end',
),
'x': (
- 'left',
- 'right',
+ 'inline-start',
+ 'inline-end',
),
'y': (
'top',
diff --git a/packages/admin-ui/src/lib/static/vendure-ui-config.json b/packages/admin-ui/src/lib/static/vendure-ui-config.json
index 2829682fc7..10617b5192 100644
--- a/packages/admin-ui/src/lib/static/vendure-ui-config.json
+++ b/packages/admin-ui/src/lib/static/vendure-ui-config.json
@@ -23,7 +23,8 @@
"uk",
"it",
"fa",
- "ne"
+ "ne",
+ "hr"
],
"brand": "",
"hideVendureBranding": false,
diff --git a/packages/asset-server-plugin/package.json b/packages/asset-server-plugin/package.json
index 6b27108c83..318d6ad8e3 100644
--- a/packages/asset-server-plugin/package.json
+++ b/packages/asset-server-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/asset-server-plugin",
- "version": "2.1.0",
+ "version": "2.1.1",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
@@ -27,8 +27,8 @@
"@types/fs-extra": "^11.0.1",
"@types/node-fetch": "^2.5.8",
"@types/sharp": "^0.30.4",
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0",
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1",
"express": "^4.17.1",
"node-fetch": "^2.6.7",
"rimraf": "^3.0.2",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 409608ff51..197fb88c0b 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/cli",
- "version": "2.1.0",
+ "version": "2.1.1",
"description": "A modern, headless ecommerce framework",
"repository": {
"type": "git",
@@ -34,7 +34,7 @@
],
"dependencies": {
"@clack/prompts": "^0.7.0",
- "@vendure/common": "^2.1.0",
+ "@vendure/common": "^2.1.1",
"change-case": "^4.1.2",
"commander": "^11.0.0",
"fs-extra": "^11.1.1",
diff --git a/packages/common/package.json b/packages/common/package.json
index 9dd9f71ca0..79c2599900 100644
--- a/packages/common/package.json
+++ b/packages/common/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/common",
- "version": "2.1.0",
+ "version": "2.1.1",
"main": "index.js",
"license": "MIT",
"scripts": {
diff --git a/packages/common/src/normalize-string.spec.ts b/packages/common/src/normalize-string.spec.ts
index 27bfca888d..b0324c8ac1 100644
--- a/packages/common/src/normalize-string.spec.ts
+++ b/packages/common/src/normalize-string.spec.ts
@@ -25,7 +25,7 @@ describe('normalizeString()', () => {
it('strips non-alphanumeric characters', () => {
expect(normalizeString('hi!!!')).toBe('hi');
expect(normalizeString('who? me?')).toBe('who me');
- expect(normalizeString('!"£$%^&*()+[]{};:@#~?/,|\\><`¬\'=')).toBe('');
+ expect(normalizeString('!"£$%^&*()+[]{};:@#~?/,|\\><`¬\'=©®™')).toBe('');
});
it('allows a subset of non-alphanumeric characters to pass through', () => {
diff --git a/packages/common/src/normalize-string.ts b/packages/common/src/normalize-string.ts
index 48aa45c676..b3fc74c814 100644
--- a/packages/common/src/normalize-string.ts
+++ b/packages/common/src/normalize-string.ts
@@ -8,6 +8,6 @@ export function normalizeString(input: string, spaceReplacer = ' '): string {
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
- .replace(/[!"£$%^&*()+[\]{};:@#~?\\/,|><`¬'=‘’]/g, '')
+ .replace(/[!"£$%^&*()+[\]{};:@#~?\\/,|><`¬'=‘’©®™]/g, '')
.replace(/\s+/g, spaceReplacer);
}
diff --git a/packages/core/e2e/custom-field-relations.e2e-spec.ts b/packages/core/e2e/custom-field-relations.e2e-spec.ts
index c92b8ca3e6..7263ddaec5 100644
--- a/packages/core/e2e/custom-field-relations.e2e-spec.ts
+++ b/packages/core/e2e/custom-field-relations.e2e-spec.ts
@@ -34,6 +34,7 @@ import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-conf
import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
import { TestPlugin1636_1664 } from './fixtures/test-plugins/issue-1636-1664/issue-1636-1664-plugin';
+import { PluginIssue2453 } from './fixtures/test-plugins/issue-2453/plugin-issue2453';
import { TestCustomEntity, WithCustomEntity } from './fixtures/test-plugins/with-custom-entity';
import { AddItemToOrderMutationVariables } from './graphql/generated-e2e-shop-types';
import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
@@ -115,7 +116,7 @@ const customConfig = mergeConfig(testConfig(), {
timezone: 'Z',
},
customFields: customFieldConfig,
- plugins: [TestPlugin1636_1664, WithCustomEntity],
+ plugins: [TestPlugin1636_1664, WithCustomEntity, PluginIssue2453],
});
describe('Custom field relations', () => {
@@ -286,6 +287,39 @@ describe('Custom field relations', () => {
assertTranslatableCustomFieldValues(product);
});
+ // https://github.com/vendure-ecommerce/vendure/issues/2453
+ it('translatable eager-loaded relation works (issue 2453)', async () => {
+ const { collections } = await adminClient.query(gql`
+ query {
+ collections(options: { sort: { name: DESC } }) {
+ totalItems
+ items {
+ id
+ name
+ customFields {
+ campaign {
+ name
+ languageCode
+ }
+ }
+ }
+ }
+ }
+ `);
+
+ expect(collections.totalItems).toBe(3);
+ expect(collections.items.find((c: any) => c.id === 'T_3')).toEqual({
+ customFields: {
+ campaign: {
+ languageCode: 'en',
+ name: 'Clearance Up to 70% Off frames',
+ },
+ },
+ id: 'T_3',
+ name: 'children collection',
+ });
+ });
+
it('ProductVariant prices get resolved', async () => {
const { product } = await adminClient.query(gql`
query {
diff --git a/packages/core/e2e/customer.e2e-spec.ts b/packages/core/e2e/customer.e2e-spec.ts
index df78313bde..e1ff171ce9 100644
--- a/packages/core/e2e/customer.e2e-spec.ts
+++ b/packages/core/e2e/customer.e2e-spec.ts
@@ -525,6 +525,22 @@ describe('Customer resolver', () => {
expect(createCustomer.message).toBe('The email address is not available.');
expect(createCustomer.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
});
+
+ it('normalizes email address on creation', async () => {
+ const { createCustomer } = await adminClient.query<
+ Codegen.CreateCustomerMutation,
+ Codegen.CreateCustomerMutationVariables
+ >(CREATE_CUSTOMER, {
+ input: {
+ emailAddress: ' JoeSmith@test.com ',
+ firstName: 'Joe',
+ lastName: 'Smith',
+ },
+ password: 'test',
+ });
+ customerErrorGuard.assertSuccess(createCustomer);
+ expect(createCustomer.emailAddress).toBe('joesmith@test.com');
+ });
});
describe('update', () => {
@@ -566,6 +582,27 @@ describe('Customer resolver', () => {
expect(me?.identifier).toBe('unique-email@test.com');
});
+
+ // https://github.com/vendure-ecommerce/vendure/issues/2449
+ it('normalizes email address on update', async () => {
+ const { updateCustomer } = await adminClient.query<
+ Codegen.UpdateCustomerMutation,
+ Codegen.UpdateCustomerMutationVariables
+ >(UPDATE_CUSTOMER, {
+ input: {
+ id: thirdCustomer.id,
+ emailAddress: ' Another-Address@test.com ',
+ },
+ });
+ customerErrorGuard.assertSuccess(updateCustomer);
+
+ expect(updateCustomer.emailAddress).toBe('another-address@test.com');
+
+ await shopClient.asUserWithCredentials('another-address@test.com', 'test');
+ const { me } = await shopClient.query
(ME);
+
+ expect(me?.identifier).toBe('another-address@test.com');
+ });
});
describe('deletion', () => {
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/api/index.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/api/index.ts
new file mode 100644
index 0000000000..f01f20e29a
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/api/index.ts
@@ -0,0 +1,21 @@
+import { gql } from 'graphql-tag';
+
+export const apiExtensions = gql`
+ type Campaign implements Node {
+ id: ID!
+ createdAt: DateTime!
+ updatedAt: DateTime!
+ name: String
+ code: String!
+ promotion: Promotion
+ promotionId: ID
+ languageCode: LanguageCode!
+ translations: [CampaignTranslation!]!
+ }
+
+ type CampaignTranslation implements Node {
+ id: ID!
+ languageCode: LanguageCode!
+ name: String!
+ }
+`;
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/campaign-translation.entity.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/campaign-translation.entity.ts
new file mode 100644
index 0000000000..eafbdc8104
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/campaign-translation.entity.ts
@@ -0,0 +1,27 @@
+import type { Translation } from '@vendure/core';
+import { DeepPartial, LanguageCode, VendureEntity } from '@vendure/core';
+import { Column, Entity, Relation, ManyToOne } from 'typeorm';
+
+import { Campaign } from './campaign.entity';
+
+/**
+ * @description This entity represents a front end campaign
+ *
+ * @docsCategory entities
+ */
+@Entity('campaign_translation')
+export class CampaignTranslation extends VendureEntity implements Translation {
+ constructor(input?: DeepPartial>) {
+ super(input);
+ }
+ @Column('varchar')
+ languageCode: LanguageCode;
+
+ @Column('varchar')
+ name: string;
+
+ @ManyToOne(() => Campaign, base => base.translations, {
+ onDelete: 'CASCADE',
+ })
+ base: Relation;
+}
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/campaign.entity.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/campaign.entity.ts
new file mode 100644
index 0000000000..deaf263c17
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/campaign.entity.ts
@@ -0,0 +1,33 @@
+import type { ID, LocaleString, Translation } from '@vendure/core';
+import { DeepPartial, Promotion, VendureEntity } from '@vendure/core';
+import { Column, Entity, ManyToOne, OneToMany } from 'typeorm';
+
+import { CampaignTranslation } from './campaign-translation.entity';
+
+/**
+ * @description This entity represents a front end campaign
+ *
+ * @docsCategory entities
+ */
+@Entity('campaign')
+export class Campaign extends VendureEntity {
+ constructor(input?: DeepPartial) {
+ super(input);
+ }
+
+ @Column({ unique: true })
+ code: string;
+
+ name: LocaleString;
+
+ @ManyToOne(() => Promotion, { onDelete: 'SET NULL' })
+ promotion: Promotion | null;
+
+ @Column('int', { nullable: true })
+ promotionId: ID | null;
+
+ @OneToMany(() => CampaignTranslation, translation => translation.base, {
+ eager: true,
+ })
+ translations: Array>;
+}
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/custom-fields-collection.entity.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/custom-fields-collection.entity.ts
new file mode 100644
index 0000000000..9fb9a86400
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/entities/custom-fields-collection.entity.ts
@@ -0,0 +1,51 @@
+import type { CustomFieldConfig } from '@vendure/core';
+import { LanguageCode } from '@vendure/core';
+
+import { Campaign } from './campaign.entity.js';
+
+/**
+ * `Collection` basic custom fields for `campaign`
+ */
+export const collectionCustomFields: CustomFieldConfig[] = [
+ {
+ type: 'relation',
+ name: 'campaign',
+ nullable: true,
+ entity: Campaign,
+ eager: true,
+ // 当shop-api必须定义schema `Campaign` type,申明, 因为public=true
+ public: true,
+ graphQLType: 'Campaign',
+ label: [
+ {
+ languageCode: LanguageCode.en,
+ value: 'Campaign',
+ },
+ ],
+ description: [
+ {
+ languageCode: LanguageCode.en,
+ value: 'Campaign of this collection page',
+ },
+ ],
+ },
+ {
+ name: 'invisible',
+ type: 'boolean',
+ public: true,
+ nullable: true,
+ defaultValue: false,
+ label: [
+ {
+ languageCode: LanguageCode.en,
+ value: 'Invisible',
+ },
+ ],
+ description: [
+ {
+ languageCode: LanguageCode.en,
+ value: 'This flag indicates if current collection is visible or inVisible, against `public`',
+ },
+ ],
+ },
+];
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/plugin-issue2453.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/plugin-issue2453.ts
new file mode 100644
index 0000000000..7edca04861
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/plugin-issue2453.ts
@@ -0,0 +1,30 @@
+import { PluginCommonModule, VendurePlugin } from '@vendure/core';
+
+import { apiExtensions } from './api/index';
+import { CampaignTranslation } from './entities/campaign-translation.entity';
+import { Campaign } from './entities/campaign.entity';
+import { collectionCustomFields } from './entities/custom-fields-collection.entity';
+import { CampaignService } from './services/campaign.service';
+
+@VendurePlugin({
+ imports: [PluginCommonModule],
+ entities: [Campaign, CampaignTranslation],
+ adminApiExtensions: {
+ schema: apiExtensions,
+ },
+ shopApiExtensions: {
+ schema: apiExtensions,
+ },
+ compatibility: '>=2.0.0',
+ providers: [CampaignService],
+ configuration: config => {
+ config.customFields.Collection.push(...collectionCustomFields);
+ return config;
+ },
+})
+export class PluginIssue2453 {
+ constructor(private campaignService: CampaignService) {}
+ async onApplicationBootstrap() {
+ await this.campaignService.initCampaigns();
+ }
+}
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/services/campaign.service.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/services/campaign.service.ts
new file mode 100644
index 0000000000..bba6ab1b20
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/services/campaign.service.ts
@@ -0,0 +1,161 @@
+import { Injectable } from '@nestjs/common';
+import { DeletionResponse, DeletionResult } from '@vendure/common/lib/generated-types';
+import type { ID, ListQueryOptions, PaginatedList, Translated } from '@vendure/core';
+import {
+ assertFound,
+ CollectionService,
+ LanguageCode,
+ ListQueryBuilder,
+ RequestContext,
+ TransactionalConnection,
+ TranslatableSaver,
+ translateDeep,
+} from '@vendure/core';
+import { In } from 'typeorm';
+
+import { CampaignTranslation } from '../entities/campaign-translation.entity';
+import { Campaign } from '../entities/campaign.entity';
+
+import { defaultCampaignData } from './default-campaigns/index';
+
+@Injectable()
+export class CampaignService {
+ constructor(
+ private readonly connection: TransactionalConnection,
+ private readonly listQueryBuilder: ListQueryBuilder,
+ private readonly collectionService: CollectionService,
+ private readonly translatableSaver: TranslatableSaver,
+ ) {}
+
+ async initCampaigns() {
+ const item = await this.makeSureDefaultCampaigns();
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ await this.makeSureDefaultCollections(item!);
+ }
+
+ findAll(
+ ctx: RequestContext,
+ options?: ListQueryOptions,
+ ): Promise>> {
+ return this.listQueryBuilder
+ .build(Campaign, options, { relations: ['promotion'], ctx })
+ .getManyAndCount()
+ .then(([campaignItems, totalItems]) => {
+ const items = campaignItems.map(campaignItem =>
+ translateDeep(campaignItem, ctx.languageCode, ['promotion']),
+ );
+ return {
+ items,
+ totalItems,
+ };
+ });
+ }
+
+ findOne(ctx: RequestContext, id: ID): Promise | undefined | null> {
+ return this.connection
+ .getRepository(ctx, Campaign)
+ .findOne({ where: { id }, relations: ['promotion'] })
+ .then(campaignItem => {
+ return campaignItem && translateDeep(campaignItem, ctx.languageCode, ['promotion']);
+ });
+ }
+
+ findOneByCode(ctx: RequestContext, code: string): Promise | undefined | null> {
+ return this.connection
+ .getRepository(ctx, Campaign)
+ .findOne({ where: { code }, relations: ['promotion'] })
+ .then(campaignItem => {
+ return campaignItem && translateDeep(campaignItem, ctx.languageCode, ['promotion']);
+ });
+ }
+
+ async create(ctx: RequestContext, input: any): Promise> {
+ const campaignItem = await this.translatableSaver.create({
+ ctx,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ input,
+ entityType: Campaign,
+ translationType: CampaignTranslation,
+ });
+ return assertFound(this.findOne(ctx, campaignItem.id));
+ }
+
+ async update(ctx: RequestContext, input: any): Promise> {
+ const campaignItem = await this.translatableSaver.update({
+ ctx,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ input,
+ entityType: Campaign,
+ translationType: CampaignTranslation,
+ });
+ return assertFound(this.findOne(ctx, campaignItem.id));
+ }
+
+ async delete(ctx: RequestContext, ids: ID[]): Promise {
+ const items = await this.connection.getRepository(ctx, Campaign).find({
+ where: {
+ id: In(ids),
+ },
+ });
+ await this.connection.getRepository(ctx, Campaign).delete(items.map(s => String(s.id)));
+
+ return {
+ result: DeletionResult.DELETED,
+ message: '',
+ };
+ }
+
+ private async makeSureDefaultCampaigns() {
+ const ctx = RequestContext.empty();
+ const { items } = await this.findAll(ctx);
+ let item;
+ for (const campaignItem of defaultCampaignData()) {
+ const hasOne = items.find(s => s.code === campaignItem.code);
+ if (!hasOne) {
+ item = await this.create(ctx, campaignItem);
+ } else {
+ item = await this.update(ctx, {
+ ...campaignItem,
+ id: hasOne.id,
+ });
+ }
+ }
+ return item;
+ }
+
+ private async makeSureDefaultCollections(campaign: Translated) {
+ const ctx = RequestContext.empty();
+ const { totalItems } = await this.collectionService.findAll(ctx);
+ if (totalItems > 0) {
+ return;
+ }
+ const parent = await this.collectionService.create(ctx, {
+ filters: [],
+ translations: [
+ {
+ name: 'parent collection',
+ slug: 'parent-collection',
+ description: 'parent collection description',
+ languageCode: LanguageCode.en,
+ customFields: {},
+ },
+ ],
+ });
+ await this.collectionService.create(ctx, {
+ filters: [],
+ parentId: parent?.id,
+ customFields: {
+ campaignId: campaign?.id,
+ },
+ translations: [
+ {
+ name: 'children collection',
+ slug: 'children-collection',
+ description: 'children collection description',
+ languageCode: LanguageCode.en,
+ customFields: {},
+ },
+ ],
+ });
+ }
+}
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/services/default-campaigns/default-campaigns.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/services/default-campaigns/default-campaigns.ts
new file mode 100644
index 0000000000..c402876ba7
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/services/default-campaigns/default-campaigns.ts
@@ -0,0 +1,23 @@
+import { LanguageCode } from '@vendure/common/lib/generated-types';
+
+export const defaultCampaigns = () =>
+ [
+ {
+ code: 'discount',
+ campaignType: 'DirectDiscount',
+ needClaimCoupon: false,
+ enabled: true,
+ translations: [
+ {
+ languageCode: LanguageCode.en,
+ name: 'Clearance Up to 70% Off frames',
+ shortDesc: 'Clearance Up to 70% Off frames',
+ },
+ {
+ languageCode: LanguageCode.de,
+ name: 'Clearance Up to 70% Off frames of de',
+ shortDesc: 'Clearance Up to 70% Off frames of de',
+ },
+ ],
+ },
+ ] as any[];
diff --git a/packages/core/e2e/fixtures/test-plugins/issue-2453/services/default-campaigns/index.ts b/packages/core/e2e/fixtures/test-plugins/issue-2453/services/default-campaigns/index.ts
new file mode 100644
index 0000000000..31fab4a66d
--- /dev/null
+++ b/packages/core/e2e/fixtures/test-plugins/issue-2453/services/default-campaigns/index.ts
@@ -0,0 +1,3 @@
+import { defaultCampaigns } from './default-campaigns';
+
+export const defaultCampaignData = () => [...defaultCampaigns()];
diff --git a/packages/core/e2e/order-promotion.e2e-spec.ts b/packages/core/e2e/order-promotion.e2e-spec.ts
index 669e5301fb..359aab889b 100644
--- a/packages/core/e2e/order-promotion.e2e-spec.ts
+++ b/packages/core/e2e/order-promotion.e2e-spec.ts
@@ -1663,7 +1663,7 @@ describe('Promotions applied to Orders', () => {
});
});
- describe.only('usage limit', () => {
+ describe('usage limit', () => {
const TEST_COUPON_CODE = 'TESTCOUPON';
const orderGuard: ErrorResultGuard =
createErrorResultGuard(input => !!input.lines);
diff --git a/packages/core/e2e/shop-auth.e2e-spec.ts b/packages/core/e2e/shop-auth.e2e-spec.ts
index 098f469656..c845e46cca 100644
--- a/packages/core/e2e/shop-auth.e2e-spec.ts
+++ b/packages/core/e2e/shop-auth.e2e-spec.ts
@@ -1263,6 +1263,29 @@ describe('Updating email address without email verification', () => {
);
expect(activeCustomer!.emailAddress).toBe(NEW_EMAIL_ADDRESS);
});
+
+ it('normalizes updated email address', async () => {
+ await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, 'test');
+ const { requestUpdateCustomerEmailAddress } = await shopClient.query<
+ CodegenShop.RequestUpdateEmailAddressMutation,
+ CodegenShop.RequestUpdateEmailAddressMutationVariables
+ >(REQUEST_UPDATE_EMAIL_ADDRESS, {
+ password: 'test',
+ newEmailAddress: ' Not.Normal@test.com ',
+ });
+ successErrorGuard.assertSuccess(requestUpdateCustomerEmailAddress);
+ // Attempting to fix flakiness possibly caused by race condition on the event
+ // subscriber
+ await new Promise(resolve => setTimeout(resolve, 100));
+ expect(requestUpdateCustomerEmailAddress.success).toBe(true);
+ expect(sendEmailFn).toHaveBeenCalledTimes(1);
+ expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
+
+ const { activeCustomer } = await shopClient.query(
+ GET_ACTIVE_CUSTOMER,
+ );
+ expect(activeCustomer!.emailAddress).toBe('not.normal@test.com');
+ });
});
function getVerificationTokenPromise(): Promise {
diff --git a/packages/core/package.json b/packages/core/package.json
index 1c4898183f..7483451985 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/core",
- "version": "2.1.0",
+ "version": "2.1.1",
"description": "A modern, headless ecommerce framework",
"repository": {
"type": "git",
@@ -50,7 +50,7 @@
"@nestjs/testing": "10.2.1",
"@nestjs/typeorm": "10.0.0",
"@types/fs-extra": "^9.0.1",
- "@vendure/common": "^2.1.0",
+ "@vendure/common": "^2.1.1",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"chalk": "^4.1.2",
diff --git a/packages/core/src/api/common/configurable-operation-codec.ts b/packages/core/src/api/common/configurable-operation-codec.ts
index a8e920530d..170ba9761f 100644
--- a/packages/core/src/api/common/configurable-operation-codec.ts
+++ b/packages/core/src/api/common/configurable-operation-codec.ts
@@ -43,8 +43,7 @@ export class ConfigurableOperationCodec {
const decodedIds = ids.map(id => this.idCodecService.decode(id));
arg.value = JSON.stringify(decodedIds);
} else {
- const decodedId = this.idCodecService.decode(arg.value);
- arg.value = JSON.stringify(decodedId);
+ arg.value = this.idCodecService.decode(arg.value);
}
}
}
diff --git a/packages/core/src/api/common/custom-field-relation-resolver.service.ts b/packages/core/src/api/common/custom-field-relation-resolver.service.ts
index 27ebba13ec..c008731fb6 100644
--- a/packages/core/src/api/common/custom-field-relation-resolver.service.ts
+++ b/packages/core/src/api/common/custom-field-relation-resolver.service.ts
@@ -55,6 +55,14 @@ export class CustomFieldRelationResolverService {
const result = fieldDef.list ? await qb.getMany() : await qb.getOne();
+ return await this.translateEntity(ctx, result, fieldDef);
+ }
+
+ async translateEntity(
+ ctx: RequestContext,
+ result: VendureEntity | VendureEntity[] | null,
+ fieldDef: RelationCustomFieldConfig,
+ ) {
if (fieldDef.entity === ProductVariant) {
if (Array.isArray(result)) {
await Promise.all(result.map(r => this.applyVariantPrices(ctx, r as any)));
diff --git a/packages/core/src/api/config/generate-resolvers.ts b/packages/core/src/api/config/generate-resolvers.ts
index 065c747294..bfde3f677c 100644
--- a/packages/core/src/api/config/generate-resolvers.ts
+++ b/packages/core/src/api/config/generate-resolvers.ts
@@ -207,10 +207,12 @@ function generateCustomFieldRelationResolvers(
args: any,
context: any,
) => {
- if (source[fieldDef.name] != null) {
- return source[fieldDef.name];
- }
const ctx: RequestContext = context.req[REQUEST_CONTEXT_KEY];
+ const eagerEntity = source[fieldDef.name];
+ // If the relation is eager-loaded, we can simply try to translate this relation entity if they have translations
+ if (eagerEntity != null) {
+ return customFieldRelationResolverService.translateEntity(ctx, eagerEntity, fieldDef);
+ }
const entityId = source[ENTITY_ID_KEY];
return customFieldRelationResolverService.resolveRelation({
ctx,
diff --git a/packages/core/src/config/shipping-method/shipping-calculator.ts b/packages/core/src/config/shipping-method/shipping-calculator.ts
index 2b20ae62e3..8390b536f6 100644
--- a/packages/core/src/config/shipping-method/shipping-calculator.ts
+++ b/packages/core/src/config/shipping-method/shipping-calculator.ts
@@ -32,7 +32,7 @@ export interface ShippingCalculatorConfig extends Configur
ui: { component: 'number-form-input', suffix: '%' },
},
* },
- * calculate: (order, args) => {
+ * calculate: (ctx, order, args) => {
* return {
* price: args.rate,
* taxRate: args.taxRate,
@@ -59,7 +59,12 @@ export class ShippingCalculator extends Confi
*
* @internal
*/
- calculate(ctx: RequestContext, order: Order, args: ConfigArg[], method: ShippingMethod): CalculateShippingFnResult {
+ calculate(
+ ctx: RequestContext,
+ order: Order,
+ args: ConfigArg[],
+ method: ShippingMethod,
+ ): CalculateShippingFnResult {
return this.calculateFn(ctx, order, this.argsArrayToHash(args), method);
}
}
@@ -115,5 +120,5 @@ export type CalculateShippingFn = (
ctx: RequestContext,
order: Order,
args: ConfigArgValues,
- method: ShippingMethod
+ method: ShippingMethod,
) => CalculateShippingFnResult;
diff --git a/packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts b/packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
index 3e5313e50a..6316b64aba 100644
--- a/packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
+++ b/packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
@@ -16,16 +16,38 @@ import { HydrateOptions } from './entity-hydrator-types';
/**
* @description
* This is a helper class which is used to "hydrate" entity instances, which means to populate them
- * with the specified relations. This is useful when writing plugin code which receives an entity
+ * with the specified relations. This is useful when writing plugin code which receives an entity,
* and you need to ensure that one or more relations are present.
*
* @example
* ```ts
- * const product = await this.productVariantService
- * .getProductForVariant(ctx, variantId);
+ * import { Injectable } from '\@nestjs/common';
+ * import { ID, RequestContext, EntityHydrator, ProductVariantService } from '\@vendure/core';
*
- * await this.entityHydrator
- * .hydrate(ctx, product, { relations: ['facetValues.facet' ]});
+ * \@Injectable()
+ * export class MyService {
+ *
+ * constructor(
+ * // highlight-next-line
+ * private entityHydrator: EntityHydrator,
+ * private productVariantService: ProductVariantService,
+ * ) {}
+ *
+ * myMethod(ctx: RequestContext, variantId: ID) {
+ * const product = await this.productVariantService
+ * .getProductForVariant(ctx, variantId);
+ *
+ * // at this stage, we don't know which of the Product relations
+ * // will be joined at runtime.
+ *
+ * // highlight-start
+ * await this.entityHydrator
+ * .hydrate(ctx, product, { relations: ['facetValues.facet' ]});
+ *
+ * // You can be sure now that the `facetValues` & `facetValues.facet` relations are populated
+ * // highlight-end
+ * }
+ * }
*```
*
* In this above example, the `product` instance will now have the `facetValues` relation
diff --git a/packages/core/src/service/services/customer.service.ts b/packages/core/src/service/services/customer.service.ts
index 2bffe9dec9..039166ef6f 100644
--- a/packages/core/src/service/services/customer.service.ts
+++ b/packages/core/src/service/services/customer.service.ts
@@ -308,13 +308,16 @@ export class CustomerService {
});
if (hasEmailAddress(input)) {
+ input.emailAddress = normalizeEmailAddress(input.emailAddress);
if (input.emailAddress !== customer.emailAddress) {
const existingCustomerInChannel = await this.connection
.getRepository(ctx, Customer)
.createQueryBuilder('customer')
.leftJoin('customer.channels', 'channel')
.where('channel.id = :channelId', { channelId: ctx.channelId })
- .andWhere('customer.emailAddress = :emailAddress', { emailAddress: input.emailAddress })
+ .andWhere('customer.emailAddress = :emailAddress', {
+ emailAddress: input.emailAddress,
+ })
.andWhere('customer.id != :customerId', { customerId: input.id })
.andWhere('customer.deletedAt is null')
.getOne();
@@ -566,6 +569,7 @@ export class CustomerService {
userId: ID,
newEmailAddress: string,
): Promise {
+ const normalizedEmailAddress = normalizeEmailAddress(newEmailAddress);
const userWithConflictingIdentifier = await this.userService.getUserByEmailAddress(
ctx,
newEmailAddress,
@@ -588,18 +592,18 @@ export class CustomerService {
type: HistoryEntryType.CUSTOMER_EMAIL_UPDATE_REQUESTED,
data: {
oldEmailAddress,
- newEmailAddress,
+ newEmailAddress: normalizedEmailAddress,
},
});
if (this.configService.authOptions.requireVerification) {
- user.getNativeAuthenticationMethod().pendingIdentifier = newEmailAddress;
+ user.getNativeAuthenticationMethod().pendingIdentifier = normalizedEmailAddress;
await this.userService.setIdentifierChangeToken(ctx, user);
this.eventBus.publish(new IdentifierChangeRequestEvent(ctx, user));
return true;
} else {
const oldIdentifier = user.identifier;
- user.identifier = newEmailAddress;
- customer.emailAddress = newEmailAddress;
+ user.identifier = normalizedEmailAddress;
+ customer.emailAddress = normalizedEmailAddress;
await this.connection.getRepository(ctx, User).save(user, { reload: false });
await this.connection.getRepository(ctx, Customer).save(customer, { reload: false });
this.eventBus.publish(new IdentifierChangeEvent(ctx, user, oldIdentifier));
@@ -609,7 +613,7 @@ export class CustomerService {
type: HistoryEntryType.CUSTOMER_EMAIL_UPDATE_VERIFIED,
data: {
oldEmailAddress,
- newEmailAddress,
+ newEmailAddress: normalizedEmailAddress,
},
});
return true;
diff --git a/packages/core/src/service/services/product.service.ts b/packages/core/src/service/services/product.service.ts
index b5f2dbd98a..eedbde2b17 100644
--- a/packages/core/src/service/services/product.service.ts
+++ b/packages/core/src/service/services/product.service.ts
@@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common';
import {
AssignProductsToChannelInput,
CreateProductInput,
- CustomerListOptions,
DeletionResponse,
DeletionResult,
ProductListOptions,
@@ -41,12 +40,9 @@ import { TranslatorService } from '../helpers/translator/translator.service';
import { AssetService } from './asset.service';
import { ChannelService } from './channel.service';
-import { CollectionService } from './collection.service';
import { FacetValueService } from './facet-value.service';
import { ProductOptionGroupService } from './product-option-group.service';
import { ProductVariantService } from './product-variant.service';
-import { RoleService } from './role.service';
-import { TaxRateService } from './tax-rate.service';
/**
* @description
@@ -61,12 +57,9 @@ export class ProductService {
constructor(
private connection: TransactionalConnection,
private channelService: ChannelService,
- private roleService: RoleService,
private assetService: AssetService,
private productVariantService: ProductVariantService,
private facetValueService: FacetValueService,
- private taxRateService: TaxRateService,
- private collectionService: CollectionService,
private listQueryBuilder: ListQueryBuilder,
private translatableSaver: TranslatableSaver,
private eventBus: EventBus,
@@ -120,12 +113,7 @@ export class ProductService {
effectiveRelations.push('facetValues.facet');
}
const product = await this.connection.findOneInChannel(ctx, Product, productId, ctx.channelId, {
- // relations: unique(effectiveRelations),
- relations: {
- facetValues: {
- facet: true,
- },
- },
+ relations: unique(effectiveRelations),
where: {
deletedAt: IsNull(),
},
diff --git a/packages/create/package.json b/packages/create/package.json
index 2f8d9a596d..bf8aa2a55b 100644
--- a/packages/create/package.json
+++ b/packages/create/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/create",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"bin": {
"create": "./index.js"
@@ -28,14 +28,14 @@
"@types/fs-extra": "^9.0.1",
"@types/handlebars": "^4.1.0",
"@types/semver": "^6.2.2",
- "@vendure/core": "^2.1.0",
+ "@vendure/core": "^2.1.1",
"rimraf": "^3.0.2",
"ts-node": "^10.9.1",
"typescript": "4.9.5"
},
"dependencies": {
"@clack/prompts": "^0.7.0",
- "@vendure/common": "^2.1.0",
+ "@vendure/common": "^2.1.1",
"commander": "^11.0.0",
"cross-spawn": "^7.0.3",
"detect-port": "^1.5.1",
diff --git a/packages/dev-server/dev-config.ts b/packages/dev-server/dev-config.ts
index ea20c748bc..7cb3fc7c12 100644
--- a/packages/dev-server/dev-config.ts
+++ b/packages/dev-server/dev-config.ts
@@ -105,21 +105,10 @@ export const devConfig: VendureConfig = {
// outputPath: path.join(__dirname, './custom-admin-ui'),
// extensions: [
// {
- // id: 'test-ui-extension',
- // extensionPath: path.join(__dirname, 'test-plugins/with-ui-extension/ui'),
- // ngModules: [
- // {
- // type: 'lazy',
- // route: 'greetz',
- // ngModuleFileName: 'greeter.module.ts',
- // ngModuleName: 'GreeterModule',
- // },
- // {
- // type: 'shared',
- // ngModuleFileName: 'greeter-shared.module.ts',
- // ngModuleName: 'GreeterSharedModule',
- // },
- // ],
+ // id: 'ui-extensions-library',
+ // extensionPath: path.join(__dirname, 'example-plugins/ui-extensions-library/ui'),
+ // routes: [{ route: 'ui-library', filePath: 'routes.ts' }],
+ // providers: ['providers.ts'],
// },
// {
// globalStyles: path.join(
@@ -127,24 +116,6 @@ export const devConfig: VendureConfig = {
// 'test-plugins/with-ui-extension/ui/custom-theme.scss',
// ),
// },
- // {
- // id: 'external-ui-extension',
- // extensionPath: path.join(__dirname, 'test-plugins/with-external-ui-extension'),
- // ngModules: [
- // {
- // type: 'lazy',
- // route: 'greet',
- // ngModuleFileName: 'external-ui-extension.ts',
- // ngModuleName: 'ExternalUiExtensionModule',
- // },
- // ],
- // staticAssets: [
- // {
- // path: path.join(__dirname, 'test-plugins/with-external-ui-extension/app'),
- // rename: 'external-app',
- // },
- // ],
- // },
// ],
// devMode: true,
// }),
@@ -192,7 +163,7 @@ function getDbConfig(): DataSourceOptions {
port: 3306,
username: 'root',
password: '',
- database: 'vendure2-dev',
+ database: 'vendure-dev',
};
}
}
diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json
index 1aa2769554..caea610c79 100644
--- a/packages/dev-server/package.json
+++ b/packages/dev-server/package.json
@@ -1,6 +1,6 @@
{
"name": "dev-server",
- "version": "2.1.0",
+ "version": "2.1.1",
"main": "index.js",
"license": "MIT",
"private": true,
@@ -15,18 +15,18 @@
},
"dependencies": {
"@nestjs/axios": "^3.0.0",
- "@vendure/admin-ui-plugin": "^2.1.0",
- "@vendure/asset-server-plugin": "^2.1.0",
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0",
- "@vendure/elasticsearch-plugin": "^2.1.0",
- "@vendure/email-plugin": "^2.1.0",
+ "@vendure/admin-ui-plugin": "^2.1.1",
+ "@vendure/asset-server-plugin": "^2.1.1",
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1",
+ "@vendure/elasticsearch-plugin": "^2.1.1",
+ "@vendure/email-plugin": "^2.1.1",
"typescript": "4.9.5"
},
"devDependencies": {
"@types/csv-stringify": "^3.1.0",
- "@vendure/testing": "^2.1.0",
- "@vendure/ui-devkit": "^2.1.0",
+ "@vendure/testing": "^2.1.1",
+ "@vendure/ui-devkit": "^2.1.1",
"commander": "^7.1.0",
"concurrently": "^8.2.1",
"csv-stringify": "^5.3.3",
diff --git a/packages/elasticsearch-plugin/package.json b/packages/elasticsearch-plugin/package.json
index 94da42c572..53f615cff4 100644
--- a/packages/elasticsearch-plugin/package.json
+++ b/packages/elasticsearch-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/elasticsearch-plugin",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -26,8 +26,8 @@
"fast-deep-equal": "^3.1.3"
},
"devDependencies": {
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0",
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1",
"rimraf": "^3.0.2",
"typescript": "4.9.5"
}
diff --git a/packages/email-plugin/package.json b/packages/email-plugin/package.json
index d7483ee81f..d23d7ce15e 100644
--- a/packages/email-plugin/package.json
+++ b/packages/email-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/email-plugin",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -35,8 +35,8 @@
"@types/fs-extra": "^9.0.1",
"@types/handlebars": "^4.1.0",
"@types/mjml": "^4.0.4",
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0",
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1",
"rimraf": "^3.0.2",
"typescript": "4.9.5"
}
diff --git a/packages/harden-plugin/package.json b/packages/harden-plugin/package.json
index 5b2f3231f9..0965c47fa9 100644
--- a/packages/harden-plugin/package.json
+++ b/packages/harden-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/harden-plugin",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -21,7 +21,7 @@
"graphql-query-complexity": "^0.12.0"
},
"devDependencies": {
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0"
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1"
}
}
diff --git a/packages/job-queue-plugin/package.json b/packages/job-queue-plugin/package.json
index ddb2369f02..4e9b005405 100644
--- a/packages/job-queue-plugin/package.json
+++ b/packages/job-queue-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/job-queue-plugin",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"main": "package/index.js",
"types": "package/index.d.ts",
@@ -23,8 +23,8 @@
},
"devDependencies": {
"@google-cloud/pubsub": "^2.8.0",
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0",
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1",
"bullmq": "^3.15.5",
"ioredis": "^5.3.0",
"rimraf": "^3.0.2",
diff --git a/packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts b/packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts
index 0cab90543a..87c6bced31 100644
--- a/packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts
+++ b/packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts
@@ -381,8 +381,7 @@ describe('Mollie payments (with useDynamicRedirectUrl set to true)', () => {
body: JSON.stringify({ id: mockData.mollieOrderResponse.id }),
headers: { 'Content-Type': 'application/json' },
});
- // tslint:disable-next-line:no-non-null-assertion
- const { order: adminOrder } = await adminClient.query(GET_ORDER_PAYMENTS, { id: order!.id });
+ const { order: adminOrder } = await adminClient.query(GET_ORDER_PAYMENTS, { id: order?.id });
expect(adminOrder.state).toBe('ArrangingPayment');
});
@@ -457,7 +456,7 @@ describe('Mollie payments (with useDynamicRedirectUrl set to true)', () => {
adminClient,
order.lines[0].id,
10,
- // tslint:disable-next-line:no-non-null-assertion
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
order.payments!.find(p => p.amount === 108990)!.id,
SURCHARGE_AMOUNT,
);
@@ -468,6 +467,8 @@ describe('Mollie payments (with useDynamicRedirectUrl set to true)', () => {
});
describe('Handle pay-later methods', () => {
+ // TODO: Add testcases that mock incoming webhook to: 1. Authorize payment and 2. AutoCapture payments
+
it('Should prepare a new order', async () => {
await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
const { addItemToOrder } = await shopClient.query<
diff --git a/packages/payments-plugin/e2e/stripe-payment.e2e-spec.ts b/packages/payments-plugin/e2e/stripe-payment.e2e-spec.ts
index 7bebbfcef7..a4bfe62d5d 100644
--- a/packages/payments-plugin/e2e/stripe-payment.e2e-spec.ts
+++ b/packages/payments-plugin/e2e/stripe-payment.e2e-spec.ts
@@ -10,7 +10,9 @@ import { CREATE_PRODUCT, CREATE_PRODUCT_VARIANTS } from '@vendure/core/e2e/graph
import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing';
import gql from 'graphql-tag';
import nock from 'nock';
+import fetch from 'node-fetch';
import path from 'path';
+import { Stripe } from 'stripe';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { initialData } from '../../../e2e-common/e2e-initial-data';
@@ -293,6 +295,101 @@ describe('Stripe payments', () => {
});
});
+ // https://github.com/vendure-ecommerce/vendure/issues/2450
+ it('Should not crash on signature validation failure', async () => {
+ const MOCKED_WEBHOOK_PAYLOAD = {
+ id: 'evt_0',
+ object: 'event',
+ api_version: '2022-11-15',
+ data: {
+ object: {
+ id: 'pi_0',
+ currency: 'usd',
+ status: 'succeeded',
+ },
+ },
+ livemode: false,
+ pending_webhooks: 1,
+ request: {
+ id: 'req_0',
+ idempotency_key: '00000000-0000-0000-0000-000000000000',
+ },
+ type: 'payment_intent.succeeded',
+ };
+
+ const payloadString = JSON.stringify(MOCKED_WEBHOOK_PAYLOAD, null, 2);
+
+ const result = await fetch(`http://localhost:${serverPort}/payments/stripe`, {
+ method: 'post',
+ body: payloadString,
+ headers: { 'Content-Type': 'application/json' },
+ });
+
+ // We didn't provided any signatures, it should result in a 400 - Bad request
+ expect(result.status).toEqual(400);
+ });
+
+ // TODO: Contribution welcome: test webhook handling and order settlement
+ // https://github.com/vendure-ecommerce/vendure/issues/2450
+ it("Should validate the webhook's signature properly", async () => {
+ await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
+
+ const { activeOrder } = await shopClient.query(GET_ACTIVE_ORDER);
+ order = activeOrder!;
+
+ const MOCKED_WEBHOOK_PAYLOAD = {
+ id: 'evt_0',
+ object: 'event',
+ api_version: '2022-11-15',
+ data: {
+ object: {
+ id: 'pi_0',
+ currency: 'usd',
+ metadata: {
+ orderCode: order.code,
+ orderId: parseInt(order.id.replace('T_', ''), 10),
+ channelToken: E2E_DEFAULT_CHANNEL_TOKEN,
+ },
+ amount_received: order.totalWithTax,
+ status: 'succeeded',
+ },
+ },
+ livemode: false,
+ pending_webhooks: 1,
+ request: {
+ id: 'req_0',
+ idempotency_key: '00000000-0000-0000-0000-000000000000',
+ },
+ type: 'payment_intent.succeeded',
+ };
+
+ const payloadString = JSON.stringify(MOCKED_WEBHOOK_PAYLOAD, null, 2);
+ const stripeWebhooks = new Stripe('test-api-secret', { apiVersion: '2023-08-16' }).webhooks;
+ const header = stripeWebhooks.generateTestHeaderString({
+ payload: payloadString,
+ secret: 'test-signing-secret',
+ });
+
+ const event = stripeWebhooks.constructEvent(payloadString, header, 'test-signing-secret');
+ expect(event.id).to.equal(MOCKED_WEBHOOK_PAYLOAD.id);
+ await setShipping(shopClient);
+ // Due to the `this.orderService.transitionToState(...)` fails with the internal lookup by id,
+ // we need to put the order into `ArrangingPayment` state manually before calling the webhook handler.
+ // const transitionResult = await adminClient.query(TRANSITION_TO_ARRANGING_PAYMENT, { id: order.id });
+ // expect(transitionResult.transitionOrderToState.__typename).toBe('Order')
+
+ const result = await fetch(`http://localhost:${serverPort}/payments/stripe`, {
+ method: 'post',
+ body: payloadString,
+ headers: { 'Content-Type': 'application/json', 'Stripe-Signature': header },
+ });
+
+ // I would expect to the status to be 200, but at the moment either the
+ // `orderService.transitionToState()` or the `orderService.addPaymentToOrder()`
+ // throws an error of 'error.entity-with-id-not-found'
+ expect(result.status).toEqual(200);
+ });
+
// https://github.com/vendure-ecommerce/vendure/issues/1630
describe('currencies with no fractional units', () => {
let japanProductId: string;
@@ -401,6 +498,4 @@ describe('Stripe payments', () => {
expect(createPaymentIntentPayload.currency).toBe('jpy');
});
});
-
- // TODO: Contribution welcome: test webhook handling and order settlement
});
diff --git a/packages/payments-plugin/package.json b/packages/payments-plugin/package.json
index 5fdb5aa68e..16c28c91dc 100644
--- a/packages/payments-plugin/package.json
+++ b/packages/payments-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/payments-plugin",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"main": "package/index.js",
"types": "package/index.d.ts",
@@ -46,9 +46,9 @@
"@mollie/api-client": "^3.7.0",
"@types/braintree": "^2.22.15",
"@types/localtunnel": "2.0.1",
- "@vendure/common": "^2.1.0",
- "@vendure/core": "^2.1.0",
- "@vendure/testing": "^2.1.0",
+ "@vendure/common": "^2.1.1",
+ "@vendure/core": "^2.1.1",
+ "@vendure/testing": "^2.1.1",
"braintree": "^3.16.0",
"localtunnel": "2.0.2",
"nock": "^13.1.4",
diff --git a/packages/payments-plugin/src/mollie/mollie.helpers.ts b/packages/payments-plugin/src/mollie/mollie.helpers.ts
index 0c732fae10..6b8612cc77 100644
--- a/packages/payments-plugin/src/mollie/mollie.helpers.ts
+++ b/packages/payments-plugin/src/mollie/mollie.helpers.ts
@@ -61,14 +61,16 @@ export function toMollieOrderLines(order: Order, alreadyPaid: number): CreatePar
vatAmount: toAmount(surcharge.priceWithTax - surcharge.price, order.currencyCode),
})));
// Deduct amount already paid
- lines.push({
- name: 'Already paid',
- quantity: 1,
- unitPrice: toAmount(-alreadyPaid, order.currencyCode),
- totalAmount: toAmount(-alreadyPaid, order.currencyCode),
- vatRate: String(0),
- vatAmount: toAmount(0, order.currencyCode),
- });
+ if (alreadyPaid) {
+ lines.push({
+ name: 'Already paid',
+ quantity: 1,
+ unitPrice: toAmount(-alreadyPaid, order.currencyCode),
+ totalAmount: toAmount(-alreadyPaid, order.currencyCode),
+ vatRate: String(0),
+ vatAmount: toAmount(0, order.currencyCode),
+ });
+ }
return lines;
}
diff --git a/packages/payments-plugin/src/mollie/mollie.service.ts b/packages/payments-plugin/src/mollie/mollie.service.ts
index e48b7e2051..2c9aadb43f 100644
--- a/packages/payments-plugin/src/mollie/mollie.service.ts
+++ b/packages/payments-plugin/src/mollie/mollie.service.ts
@@ -227,6 +227,13 @@ export class MollieService {
`Unable to find order ${mollieOrder.orderNumber}, unable to process Mollie order ${mollieOrder.id}`,
);
}
+ if (order.state === 'PaymentSettled') {
+ Logger.info(
+ `Order ${order.code} is already 'PaymentSettled', no need for handling Mollie status '${mollieOrder.status}'`,
+ loggerCtx,
+ );
+ return;
+ }
if (mollieOrder.status === OrderStatus.expired) {
// Expired is fine, a customer can retry the payment later
return;
@@ -248,13 +255,6 @@ export class MollieService {
if (order.state === 'PaymentAuthorized' && mollieOrder.status === OrderStatus.completed) {
return this.settleExistingPayment(ctx, order, mollieOrder.id);
}
- if (order.state === 'PaymentAuthorized' || order.state === 'PaymentSettled') {
- Logger.info(
- `Order ${order.code} is '${order.state}', no need for handling Mollie status '${mollieOrder.status}'`,
- loggerCtx,
- );
- return;
- }
// Any other combination of Mollie status and Vendure status indicates something is wrong.
throw Error(
`Unhandled incoming Mollie status '${mollieOrder.status}' for order ${order.code} with status '${order.state}'`,
@@ -308,6 +308,7 @@ export class MollieService {
* Settle an existing payment based on the given mollieOrder
*/
async settleExistingPayment(ctx: RequestContext, order: Order, mollieOrderId: string): Promise {
+ order = await this.entityHydrator.hydrate(ctx, order, { relations: ['payments'] });
const payment = order.payments.find(p => p.transactionId === mollieOrderId);
if (!payment) {
throw Error(
@@ -335,7 +336,7 @@ export class MollieService {
}
const client = createMollieClient({ apiKey });
// We use the orders API, so list available methods for that API usage
- const methods = await client.methods.list({resource: 'orders'});
+ const methods = await client.methods.list({ resource: 'orders' });
return methods.map(m => ({
...m,
code: m.id,
diff --git a/packages/payments-plugin/src/stripe/raw-body.middleware.ts b/packages/payments-plugin/src/stripe/raw-body.middleware.ts
index 03257b6748..71c21b55ac 100644
--- a/packages/payments-plugin/src/stripe/raw-body.middleware.ts
+++ b/packages/payments-plugin/src/stripe/raw-body.middleware.ts
@@ -8,6 +8,7 @@ import { RequestWithRawBody } from './types';
* Stripe to properly verify webhook events.
*/
export const rawBodyMiddleware = raw({
+ type: '*/*',
verify(req: RequestWithRawBody, res: http.ServerResponse, buf: Buffer, encoding: string) {
if (Buffer.isBuffer(buf)) {
req.rawBody = Buffer.from(buf);
diff --git a/packages/payments-plugin/src/stripe/stripe.controller.ts b/packages/payments-plugin/src/stripe/stripe.controller.ts
index 91fd4fde04..0c80a5d512 100644
--- a/packages/payments-plugin/src/stripe/stripe.controller.ts
+++ b/packages/payments-plugin/src/stripe/stripe.controller.ts
@@ -45,7 +45,7 @@ export class StripeController {
return;
}
- const event = request.body as Stripe.Event;
+ const event = JSON.parse(request.body.toString()) as Stripe.Event;
const paymentIntent = event.data.object as Stripe.PaymentIntent;
if (!paymentIntent) {
@@ -120,11 +120,20 @@ export class StripeController {
`Error adding payment to order ${orderCode}: ${addPaymentToOrderResult.message}`,
loggerCtx,
);
+ return;
}
+
+ // The payment intent ID is added to the order only if we can reach this point.
+ Logger.info(
+ `Stripe payment intent id ${paymentIntent.id} added to order ${orderCode}`,
+ loggerCtx,
+ );
});
- Logger.info(`Stripe payment intent id ${paymentIntent.id} added to order ${orderCode}`, loggerCtx);
- response.status(HttpStatus.OK).send('Ok');
+ // Send the response status only if we didn't sent anything yet.
+ if (!response.headersSent) {
+ response.status(HttpStatus.OK).send('Ok');
+ }
}
private async createContext(channelToken: string, req: RequestWithRawBody): Promise {
diff --git a/packages/testing/package.json b/packages/testing/package.json
index ad56b6c0de..14482bedf3 100644
--- a/packages/testing/package.json
+++ b/packages/testing/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/testing",
- "version": "2.1.0",
+ "version": "2.1.1",
"description": "End-to-end testing tools for Vendure projects",
"keywords": [
"vendure",
@@ -38,7 +38,7 @@
"dependencies": {
"@graphql-typed-document-node/core": "^3.2.0",
"@types/node-fetch": "^2.6.4",
- "@vendure/common": "^2.1.0",
+ "@vendure/common": "^2.1.1",
"faker": "^4.1.0",
"form-data": "^4.0.0",
"graphql": "16.8.0",
@@ -49,7 +49,7 @@
"devDependencies": {
"@types/mysql": "^2.15.15",
"@types/pg": "^7.14.5",
- "@vendure/core": "^2.1.0",
+ "@vendure/core": "^2.1.1",
"mysql": "^2.18.1",
"pg": "^8.4.0",
"rimraf": "^3.0.0",
diff --git a/packages/ui-devkit/package.json b/packages/ui-devkit/package.json
index 98b5fc6e78..7b68262844 100644
--- a/packages/ui-devkit/package.json
+++ b/packages/ui-devkit/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/ui-devkit",
- "version": "2.1.0",
+ "version": "2.1.1",
"description": "A library for authoring Vendure Admin UI extensions",
"keywords": [
"vendure",
@@ -40,8 +40,8 @@
"@angular/cli": "^16.2.0",
"@angular/compiler": "^16.2.2",
"@angular/compiler-cli": "^16.2.2",
- "@vendure/admin-ui": "^2.1.0",
- "@vendure/common": "^2.1.0",
+ "@vendure/admin-ui": "^2.1.1",
+ "@vendure/common": "^2.1.1",
"chalk": "^4.1.0",
"chokidar": "^3.5.3",
"fs-extra": "^11.1.1",
@@ -51,7 +51,7 @@
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.2.1",
"@types/fs-extra": "^11.0.1",
- "@vendure/core": "^2.1.0",
+ "@vendure/core": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rimraf": "^3.0.2",