diff --git a/.changeset/gentle-dancers-count.md b/.changeset/gentle-dancers-count.md new file mode 100644 index 0000000000000..aa70f11cab91d --- /dev/null +++ b/.changeset/gentle-dancers-count.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +fix: do not update the .env.template file with the database name diff --git a/.changeset/honest-chefs-attend.md b/.changeset/honest-chefs-attend.md new file mode 100644 index 0000000000000..ee5c391feb7bd --- /dev/null +++ b/.changeset/honest-chefs-attend.md @@ -0,0 +1,5 @@ +--- +"@medusajs/core-flows": patch +--- + +Order edit quantity diff diff --git a/.changeset/thirty-lamps-collect.md b/.changeset/thirty-lamps-collect.md new file mode 100644 index 0000000000000..9b62cebb36aff --- /dev/null +++ b/.changeset/thirty-lamps-collect.md @@ -0,0 +1,6 @@ +--- +"@medusajs/core-flows": patch +"@medusajs/medusa": patch +--- + +Create Order before payment capture diff --git a/integration-tests/http/__tests__/order-edits/order-edits.spec.ts b/integration-tests/http/__tests__/order-edits/order-edits.spec.ts index 9790780b1215c..80f4fa4d7bd6e 100644 --- a/integration-tests/http/__tests__/order-edits/order-edits.spec.ts +++ b/integration-tests/http/__tests__/order-edits/order-edits.spec.ts @@ -1,10 +1,10 @@ +import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { ContainerRegistrationKeys, Modules, OrderChangeStatus, RuleOperator, } from "@medusajs/utils" -import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { adminHeaders, createAdminUser, @@ -418,6 +418,31 @@ medusaIntegrationTestRunner({ expect(result.summary.current_order_total).toEqual(124) expect(result.summary.original_order_total).toEqual(60) + const updatedItem = result.items.find((i) => i.id === item.id) + expect(updatedItem.actions).toEqual([ + expect.objectContaining({ + details: expect.objectContaining({ + quantity: 2, + unit_price: 25, + quantity_diff: 0, + }), + }), + expect.objectContaining({ + details: expect.objectContaining({ + quantity: 3, + unit_price: 25, + quantity_diff: 1, + }), + }), + expect.objectContaining({ + details: expect.objectContaining({ + quantity: 3, + unit_price: 30, + quantity_diff: 1, + }), + }), + ]) + // Remove the item by setting the quantity to 0 result = ( await api.post( diff --git a/packages/admin/dashboard/src/i18n/languages.ts b/packages/admin/dashboard/src/i18n/languages.ts index 9ef2d24e3b433..b78f4b3fff48e 100644 --- a/packages/admin/dashboard/src/i18n/languages.ts +++ b/packages/admin/dashboard/src/i18n/languages.ts @@ -1,4 +1,4 @@ -import { enUS, pl } from "date-fns/locale" +import { de, enUS, pl } from "date-fns/locale" import { Language } from "./types" export const languages: Language[] = [ @@ -8,9 +8,15 @@ export const languages: Language[] = [ ltr: true, date_locale: enUS, }, + { + code: "de", + display_name: "Deutsch", + ltr: true, + date_locale: de, + }, { code: "pl", - display_name: "Polish", + display_name: "Polski", ltr: true, date_locale: pl, }, diff --git a/packages/admin/dashboard/src/i18n/translations/de.json b/packages/admin/dashboard/src/i18n/translations/de.json new file mode 100644 index 0000000000000..f433dfc43537d --- /dev/null +++ b/packages/admin/dashboard/src/i18n/translations/de.json @@ -0,0 +1,2755 @@ +{ + "$schema": "./$schema.json", + "general": { + "ascending": "Aufsteigend", + "descending": "Absteigend", + "add": "Hinzufügen", + "start": "Start", + "end": "Ende", + "open": "Offen", + "close": "Schließen", + "apply": "Anwenden", + "range": "Reichweite", + "search": "Suchen", + "of": "von", + "results": "Ergebnisse", + "pages": "Seiten", + "next": "Nächste", + "prev": "Vorher", + "is": "Ist", + "timeline": "Zeitleiste", + "success": "Erfolg", + "warning": "Warnung", + "tip": "Tipp", + "error": "Fehler", + "select": "Wählen", + "selected": "Ausgewählt", + "enabled": "Ermöglicht", + "disabled": "Deaktiviert", + "expired": "Abgelaufen", + "active": "Aktiv", + "revoked": "Widerrufen", + "new": "Neu", + "modified": "Geändert", + "added": "Hinzugefügt", + "removed": "ENTFERNT", + "admin": "Admin", + "store": "Speichern", + "details": "Einzelheiten", + "items_one": "{{count}} Artikel", + "items_other": "{{count}} Artikel", + "countSelected": "{{count}} ausgewählt", + "countOfTotalSelected": "{{count}} von {{total}} ausgewählt", + "plusCount": "+ {{count}}", + "plusCountMore": "+ {{count}} mehr", + "areYouSure": "Bist du sicher?", + "noRecordsFound": "Keine Datensätze gefunden", + "typeToConfirm": "Bitte geben Sie zur Bestätigung {val} ein:", + "noResultsTitle": "Keine Ergebnisse", + "noResultsMessage": "Versuchen Sie, die Filter oder die Suchabfrage zu ändern", + "noSearchResults": "Keine Suchergebnisse", + "noSearchResultsFor": "Keine Suchergebnisse für <0>'{{query}}'", + "noRecordsTitle": "Keine Aufzeichnungen", + "noRecordsMessage": "Es sind keine Datensätze vorhanden", + "unsavedChangesTitle": "Sind Sie sicher, dass Sie dieses Formular verlassen möchten?", + "unsavedChangesDescription": "Sie haben nicht gespeicherte Änderungen, die verloren gehen, wenn Sie dieses Formular verlassen.", + "includesTaxTooltip": "Die Preise in dieser Spalte verstehen sich inklusive Mehrwertsteuer.", + "excludesTaxTooltip": "Die Preise in dieser Spalte verstehen sich exklusive Mehrwertsteuer.", + "noMoreData": "Keine Daten mehr" + }, + "json": { + "header": "JSON", + "numberOfKeys_one": "{{count}} Schlüssel", + "numberOfKeys_other": "{{count}} Schlüssel", + "drawer": { + "header_one": "JSON <0>· {{count}} Schlüssel", + "header_other": "JSON <0>· {{count}} Schlüssel", + "description": "Sehen Sie sich die JSON-Daten für dieses Objekt an." + } + }, + "metadata": { + "header": "Metadaten", + "numberOfKeys_one": "{{count}} Schlüssel", + "numberOfKeys_other": "{{count}} Schlüssel", + "edit": { + "header": "Metadaten bearbeiten", + "description": "Bearbeiten Sie die Metadaten für dieses Objekt.", + "successToast": "Metadaten wurden erfolgreich aktualisiert.", + "actions": { + "insertRowAbove": "Zeile oben einfügen", + "insertRowBelow": "Zeile unten einfügen", + "deleteRow": "Zeile löschen" + }, + "labels": { + "key": "Schlüssel", + "value": "Wert" + }, + "complexRow": { + "label": "Einige Zeilen sind deaktiviert", + "description": "Dieses Objekt enthält nicht-primitive Metadaten wie Arrays oder Objekte, die hier nicht bearbeitet werden können. Um die deaktivierten Zeilen zu bearbeiten, verwenden Sie direkt die API.", + "tooltip": "Diese Zeile ist deaktiviert, da sie nicht-primitive Daten enthält." + } + } + }, + "validation": { + "mustBeInt": "Der Wert muss eine ganze Zahl sein.", + "mustBePositive": "Der Wert muss eine positive Zahl sein." + }, + "actions": { + "save": "Speichern", + "saveAsDraft": "Als Entwurf speichern", + "copy": "Kopie", + "copied": "Kopiert", + "duplicate": "Duplikat", + "publish": "Veröffentlichen", + "create": "Erstellen", + "delete": "Löschen", + "remove": "Entfernen", + "revoke": "Widerrufen", + "cancel": "Stornieren", + "forceConfirm": "Bestätigung erzwingen", + "continueEdit": "Bearbeiten Sie weiter", + "enable": "Aktivieren", + "disable": "Deaktivieren", + "undo": "Rückgängig machen", + "complete": "Vollständig", + "viewDetails": "Details anzeigen", + "back": "Zurück", + "close": "Schließen", + "showMore": "Mehr anzeigen", + "continue": "Weitermachen", + "continueWithEmail": "Fahren Sie mit E-Mail fort", + "idCopiedToClipboard": "ID in die Zwischenablage kopiert", + "addReason": "Grund hinzufügen", + "addNote": "Notiz hinzufügen", + "reset": "Zurücksetzen", + "confirm": "Bestätigen", + "edit": "Bearbeiten", + "addItems": "Elemente hinzufügen", + "download": "Herunterladen", + "clear": "Klar", + "clearAll": "Alles löschen", + "apply": "Anwenden", + "add": "Hinzufügen", + "select": "Wählen", + "browse": "Durchsuchen", + "logout": "Abmelden", + "hide": "Verstecken", + "export": "Export", + "import": "Import" + }, + "operators": { + "in": "In" + }, + "app": { + "search": { + "label": "Suchen", + "title": "Suchen", + "description": "Durchsuchen Sie Ihren gesamten Shop, einschließlich Bestellungen, Produkte, Kunden und mehr.", + "allAreas": "Alle Bereiche", + "navigation": "Navigation", + "openResult": "Ergebnis öffnen", + "showMore": "Mehr anzeigen", + "placeholder": "Springe zu oder finde etwas...", + "noResultsTitle": "Keine Ergebnisse gefunden", + "noResultsMessage": "Wir konnten nichts finden, was Ihrer Suche entspricht.", + "emptySearchTitle": "Geben Sie ein, um zu suchen", + "emptySearchMessage": "Geben Sie ein Schlüsselwort oder eine Phrase ein, die Sie erkunden möchten.", + "loadMore": "Laden Sie {{count}} mehr", + "groups": { + "all": "Alle Bereiche", + "customer": "Kunden", + "customerGroup": "Kundengruppen", + "product": "Produkte", + "productVariant": "Produktvarianten", + "inventory": "Inventar", + "reservation": "Reservierungen", + "category": "Kategorien", + "collection": "Sammlungen", + "order": "Bestellungen", + "promotion": "Werbeaktionen", + "campaign": "Kampagnen", + "priceList": "Preislisten", + "user": "Benutzer", + "region": "Regionen", + "taxRegion": "Steuerregionen", + "returnReason": "Rückgabegründe", + "salesChannel": "Vertriebskanäle", + "productType": "Produkttypen", + "productTag": "Produkt-Tags", + "location": "Standorte", + "shippingProfile": "Versandprofile", + "publishableApiKey": "Veröffentlichbare API-Schlüssel", + "secretApiKey": "Geheime API-Schlüssel", + "command": "Befehle", + "navigation": "Navigation" + } + }, + "keyboardShortcuts": { + "pageShortcut": "Springe zu", + "settingShortcut": "Einstellungen", + "commandShortcut": "Befehle", + "then": "Dann", + "navigation": { + "goToOrders": "Bestellungen", + "goToProducts": "Produkte", + "goToCollections": "Sammlungen", + "goToCategories": "Kategorien", + "goToCustomers": "Kunden", + "goToCustomerGroups": "Kundengruppen", + "goToInventory": "Inventar", + "goToReservations": "Reservierungen", + "goToPriceLists": "Preislisten", + "goToPromotions": "Werbeaktionen", + "goToCampaigns": "Kampagnen" + }, + "settings": { + "goToSettings": "Einstellungen", + "goToStore": "Speichern", + "goToUsers": "Benutzer", + "goToRegions": "Regionen", + "goToTaxRegions": "Steuerregionen", + "goToSalesChannels": "Vertriebskanäle", + "goToProductTypes": "Produkttypen", + "goToLocations": "Standorte", + "goToPublishableApiKeys": "Veröffentlichbare API-Schlüssel", + "goToSecretApiKeys": "Geheime API-Schlüssel", + "goToWorkflows": "Arbeitsabläufe", + "goToProfile": "Profil", + "goToReturnReasons": "Rückgabegründe" + } + }, + "menus": { + "user": { + "documentation": "Dokumentation", + "changelog": "Änderungsprotokoll", + "shortcuts": "Verknüpfungen", + "profileSettings": "Profileinstellungen", + "theme": { + "label": "Thema", + "dark": "Dunkel", + "light": "Licht", + "system": "System" + } + }, + "store": { + "label": "Speichern", + "storeSettings": "Einstellungen speichern" + }, + "actions": { + "logout": "Abmelden" + } + }, + "nav": { + "accessibility": { + "title": "Navigation", + "description": "Navigationsmenü für das Dashboard." + }, + "common": { + "extensions": "Erweiterungen" + }, + "main": { + "store": "Speichern", + "storeSettings": "Einstellungen speichern" + }, + "settings": { + "header": "Einstellungen", + "general": "Allgemein", + "developer": "Entwickler", + "myAccount": "Mein Konto" + } + } + }, + "dataGrid": { + "columns": { + "view": "Sicht", + "resetToDefault": "Auf Standard zurücksetzen", + "disabled": "Das Ändern der sichtbaren Spalten ist deaktiviert." + }, + "shortcuts": { + "label": "Verknüpfungen", + "commands": { + "undo": "Rückgängig machen", + "redo": "Wiederholen", + "copy": "Kopie", + "paste": "Paste", + "edit": "Bearbeiten", + "delete": "Löschen", + "clear": "Klar", + "moveUp": "Bewegen Sie sich nach oben", + "moveDown": "Bewegen Sie sich nach unten", + "moveLeft": "Bewegen Sie sich nach links", + "moveRight": "Bewegen Sie sich nach rechts", + "moveTop": "Nach oben verschieben", + "moveBottom": "Nach unten bewegen", + "selectDown": "Wählen Sie nach unten", + "selectUp": "Wählen Sie nach oben", + "selectColumnDown": "Spalte nach unten auswählen", + "selectColumnUp": "Spalte nach oben auswählen", + "focusToolbar": "Fokus-Symbolleiste", + "focusCancel": "Fokus abbrechen" + } + }, + "errors": { + "fixError": "Fehler beheben", + "count_one": "{{count}} Fehler", + "count_other": "{{count}} Fehler" + } + }, + "filters": { + "date": { + "today": "Heute", + "lastSevenDays": "Letzte 7 Tage", + "lastThirtyDays": "Letzte 30 Tage", + "lastNinetyDays": "Letzte 90 Tage", + "lastTwelveMonths": "Letzte 12 Monate", + "custom": "Benutzerdefiniert", + "from": "Von", + "to": "Bis" + }, + "compare": { + "lessThan": "Weniger als", + "greaterThan": "Größer als", + "exact": "Genau", + "range": "Reichweite", + "lessThanLabel": "kleiner als {{value}}", + "greaterThanLabel": "größer als {{value}}", + "andLabel": "Und" + }, + "addFilter": "Filter hinzufügen" + }, + "errorBoundary": { + "badRequestTitle": "400 – Ungültige Anfrage", + "badRequestMessage": "Die Anfrage konnte aufgrund einer fehlerhaften Syntax vom Server nicht verstanden werden.", + "notFoundTitle": "404 – Unter dieser Adresse gibt es keine Seite", + "notFoundMessage": "Überprüfen Sie die URL und versuchen Sie es erneut, oder verwenden Sie die Suchleiste, um das Gesuchte zu finden.", + "internalServerErrorTitle": "500 – Interner Serverfehler", + "internalServerErrorMessage": "Auf dem Server ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.", + "defaultTitle": "Es ist ein Fehler aufgetreten", + "defaultMessage": "Beim Rendern dieser Seite ist ein unerwarteter Fehler aufgetreten.", + "noMatchMessage": "Die gesuchte Seite existiert nicht.", + "backToDashboard": "Zurück zum Dashboard" + }, + "addresses": { + "shippingAddress": { + "header": "Lieferadresse", + "editHeader": "Lieferadresse bearbeiten", + "editLabel": "Lieferadresse", + "label": "Lieferadresse" + }, + "billingAddress": { + "header": "Rechnungsadresse", + "editHeader": "Rechnungsadresse bearbeiten", + "editLabel": "Rechnungsadresse", + "label": "Rechnungsadresse", + "sameAsShipping": "Entspricht der Lieferadresse" + }, + "contactHeading": "Kontakt", + "locationHeading": "Standort" + }, + "email": { + "editHeader": "E-Mail bearbeiten", + "editLabel": "E-Mail", + "label": "E-Mail" + }, + "transferOwnership": { + "header": "Eigentum übertragen", + "label": "Eigentum übertragen", + "details": { + "order": "Bestelldetails", + "draft": "Entwurfsdetails" + }, + "currentOwner": { + "label": "Aktueller Besitzer", + "hint": "Der aktuelle Eigentümer der Bestellung." + }, + "newOwner": { + "label": "Neuer Besitzer", + "hint": "Der neue Eigentümer, an den die Bestellung übertragen werden soll." + }, + "validation": { + "mustBeDifferent": "Der neue Eigentümer muss sich vom aktuellen Eigentümer unterscheiden.", + "required": "Neuer Eigentümer ist erforderlich." + } + }, + "sales_channels": { + "availableIn": "Verfügbar in <0>{{x}} von <1>{{y}} Vertriebskanälen" + }, + "products": { + "domain": "Produkte", + "list": { + "noRecordsMessage": "Erstellen Sie Ihr erstes Produkt, um mit dem Verkauf zu beginnen." + }, + "edit": { + "header": "Produkt bearbeiten", + "description": "Produktdetails bearbeiten.", + "successToast": "Produkz {{title}} angepasst." + }, + "create": { + "header": "Allgemein", + "tabs": { + "details": "Details", + "organize": "Organisierren", + "variants": "Varianten", + "inventory": "Inventar-Kits" + }, + "errors": { + "variants": "Bitte wählen Sie mindestens eine Variante aus.", + "options": "Bitte erstellen Sie mindestens eine Option.", + "uniqueSku": "Die SKU muss eindeutig sein." + }, + "inventory": { + "heading": "Inventar-Kits", + "label": "Fügen Sie Inventargegenstände zum Inventarkit der Variante hinzu.", + "itemPlaceholder": "Wählen Sie den Inventargegenstand aus", + "quantityPlaceholder": "Wie viele davon werden für den Bausatz benötigt?" + }, + "variants": { + "header": "Varianten", + "subHeadingTitle": "Ja, es handelt sich um ein Produkt mit Varianten", + "subHeadingDescription": "Wenn diese Option deaktiviert ist, erstellen wir eine Standardvariante für Sie", + "optionTitle": { + "placeholder": "Größe" + }, + "optionValues": { + "placeholder": "Klein, Mittel, Groß" + }, + "productVariants": { + "label": "Produktvarianten", + "hint": "Dieses Ranking wirkt sich auf die Reihenfolge der Varianten in Ihrem Store aus.", + "alert": "Fügen Sie Optionen hinzu, um Varianten zu erstellen.", + "tip": "Varianten, die nicht aktiviert sind, werden nicht erstellt. Sie können nachträglich jederzeit Varianten erstellen und bearbeiten, diese Liste passt jedoch zu den Variationen in Ihren Produktoptionen." + }, + "productOptions": { + "label": "Produktoptionen", + "hint": "Definieren Sie die Optionen für das Produkt, z.B. Farbe, Größe usw." + } + }, + "successToast": "Produkt {{title}} wurde erfolgreich erstellt." + }, + "export": { + "header": "Produktliste exportieren", + "description": "Exportieren Sie die Produktliste in eine CSV-Datei.", + "success": { + "title": "Wir bearbeiten Export ab", + "description": "Der Datenexport kann einige Minuten dauern. Wir benachrichtigen Sie, wenn wir fertig sind." + }, + "filters": { + "title": "Filter", + "description": "Wenden Sie Filter in der Tabellenübersicht an, um diese Ansicht anzupassen" + }, + "columns": { + "title": "Spalten", + "description": "Passen Sie die exportierten Daten an spezifische Anforderungen an" + } + }, + "import": { + "header": "Produktliste importieren", + "uploadLabel": "Produkte importieren", + "uploadHint": "Ziehen Sie eine CSV-Datei per Drag-and-Drop oder klicken Sie zum Hochladen", + "description": "Importieren Sie Produkte, indem Sie eine CSV-Datei in einem vordefinierten Format bereitstellen", + "template": { + "title": "Sie sind sich nicht sicher, wie Sie Ihre Liste ordnen sollen?", + "description": "Laden Sie die Vorlage unten herunter, um sicherzustellen, dass Sie das richtige Format befolgen." + }, + "upload": { + "title": "Laden Sie eine CSV-Datei hoch", + "description": "Durch Importe können Sie Produkte hinzufügen oder aktualisieren. Um vorhandene Produkte zu aktualisieren, müssen Sie das vorhandene Handle und die vorhandene ID verwenden. Um vorhandene Varianten zu aktualisieren, müssen Sie die vorhandene ID verwenden. Bevor wir Produkte importieren, werden Sie um eine Bestätigung gebeten.", + "preprocessing": "Vorverarbeitung...", + "productsToCreate": "Es werden Produkte erstellt", + "productsToUpdate": "Produkte werden aktualisiert" + }, + "success": { + "title": "Wir bearbeiten Ihren Import", + "description": "Das Importieren der Daten kann eine Weile dauern. Wir benachrichtigen Sie, wenn wir fertig sind." + } + }, + "deleteWarning": "Sie sind dabei, das Produkt {{title}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "variants": "Varianten", + "attributes": "Attribute", + "editAttributes": "Attribute bearbeiten", + "editOptions": "Optionen bearbeiten", + "editPrices": "Preise bearbeiten", + "media": { + "label": "Medien", + "editHint": "Fügen Sie dem Produkt Medien hinzu, um es in Ihrem Schaufenster zu präsentieren.", + "makeThumbnail": "Miniaturbild erstellen", + "uploadImagesLabel": "Bilder hochladen", + "uploadImagesHint": "Ziehen Sie Bilder per Drag-and-Drop hierher oder klicken Sie zum Hochladen.", + "invalidFileType": "{{name}}' ist kein unterstützter Dateityp. Unterstützte Dateitypen sind: {{types}}.", + "failedToUpload": "Das Hochladen der hinzugefügten Medien ist fehlgeschlagen. Bitte versuchen Sie es erneut.", + "deleteWarning_one": "Sie sind dabei, {{count}} Bild zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "deleteWarning_other": "Sie sind dabei, {{count}} Bilder zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "deleteWarningWithThumbnail_one": "Sie sind dabei, {{count}} Bild einschließlich der Miniaturansicht zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "deleteWarningWithThumbnail_other": "Sie sind dabei, {{count}} Bilder einschließlich der Miniaturansicht zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "thumbnailTooltip": "Miniaturansicht", + "galleryLabel": "Galerie", + "downloadImageLabel": "Aktuelles Bild herunterladen", + "deleteImageLabel": "Aktuelles Bild löschen", + "emptyState": { + "header": "Noch keine Medien", + "description": "Fügen Sie dem Produkt Medien hinzu, um es in Ihrem Schaufenster zu präsentieren.", + "action": "Medien hinzufügen" + } + }, + "discountableHint": "Wenn diese Option deaktiviert ist, werden auf dieses Produkt keine Rabatte gewährt.", + "noSalesChannels": "In keinem Vertriebskanal verfügbar", + "variantCount_one": "{{count}} Variante", + "variantCount_other": "{{count}} Varianten", + "deleteVariantWarning": "Sie sind dabei, die Variante {{title}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "productStatus": { + "draft": "Entwurf", + "published": "Veröffentlicht", + "proposed": "Vorgeschlagen", + "rejected": "Abgelehnt" + }, + "fields": { + "title": { + "label": "Titel", + "hint": "Geben Sie Ihrem Produkt einen kurzen und klaren Titel.<0/>50-60 Zeichen ist die empfohlene Länge für Suchmaschinen." + }, + "subtitle": { + "label": "Untertitel" + }, + "handle": { + "label": "Handhaben", + "tooltip": "Der Handle wird verwendet, um auf das Produkt in Ihrer Storefront zu verweisen. Wenn nicht angegeben, wird das Handle aus dem Produkttitel generiert." + }, + "description": { + "label": "Beschreibung", + "hint": "Geben Sie Ihrem Produkt eine kurze und klare Beschreibung.<0/>120-160 Zeichen ist die empfohlene Länge für Suchmaschinen." + }, + "discountable": { + "label": "Rabattierbar", + "hint": "Wenn diese Option deaktiviert ist, werden auf dieses Produkt keine Rabatte gewährt" + }, + "type": { + "label": "Typ" + }, + "collection": { + "label": "Sammlung" + }, + "categories": { + "label": "Kategorien" + }, + "tags": { + "label": "Schlagworte" + }, + "sales_channels": { + "label": "Vertriebskanäle", + "hint": "Dieses Produkt ist nur dann im Standard-Vertriebskanal verfügbar, wenn es unberührt bleibt." + }, + "countryOrigin": { + "label": "Ursprungsland" + }, + "material": { + "label": "Material" + }, + "width": { + "label": "Breite" + }, + "length": { + "label": "Länge" + }, + "height": { + "label": "Höhe" + }, + "weight": { + "label": "Gewicht" + }, + "options": { + "label": "Produktoptionen", + "hint": "Mithilfe von Optionen werden Farbe, Größe usw. des Produkts definiert", + "add": "Option hinzufügen", + "optionTitle": "Optionstitel", + "optionTitlePlaceholder": "Farbe", + "variations": "Variationen (durch Kommas getrennt)", + "variantionsPlaceholder": "Rot, Blau, Grün" + }, + "variants": { + "label": "Produktvarianten", + "hint": "Varianten, die nicht aktiviert sind, werden nicht erstellt. Diese Rangfolge wirkt sich auf die Rangfolge der Varianten in Ihrem Frontend aus." + }, + "mid_code": { + "label": "Mittlerer Code" + }, + "hs_code": { + "label": "HS-Code" + } + }, + "variant": { + "edit": { + "header": "Variante bearbeiten", + "success": "Produktvariante erfolgreich bearbeitet" + }, + "create": { + "header": "Variantendetails" + }, + "deleteWarning": "Möchten Sie diese Variante wirklich löschen?", + "pricesPagination": "1 – {{current}} von {{total}} Preisen", + "tableItemAvailable": "{{availableCount}} verfügbar", + "tableItem_one": "{{availableCount}} verfügbar am Standort {{locationCount}}", + "tableItem_other": "{{availableCount}} an {{locationCount}} Standorten verfügbar", + "inventory": { + "notManaged": "Nicht verwaltet", + "manageItems": "Verwalten Sie Inventargegenstände", + "notManagedDesc": "Für diese Variante wird kein Lagerbestand verwaltet. Aktivieren Sie „Inventar verwalten“, um den Bestand der Variante zu verfolgen.", + "manageKit": "Inventar-Kit verwalten", + "navigateToItem": "Gehen Sie zum Inventargegenstand", + "actions": { + "inventoryItems": "Gehen Sie zum Inventargegenstand", + "inventoryKit": "Inventargegenstände anzeigen" + }, + "inventoryKit": "Inventar-Kit", + "inventoryKitHint": "Besteht diese Variante aus mehreren Inventargegenständen?", + "validation": { + "itemId": "Bitte wählen Sie den Inventarartikel aus.", + "quantity": "Menge ist erforderlich. Bitte geben Sie eine positive Zahl ein." + }, + "header": "Lager und Inventar", + "editItemDetails": "Artikeldetails bearbeiten", + "manageInventoryLabel": "Inventar verwalten", + "manageInventoryHint": "Wenn diese Option aktiviert ist, ändern wir die Lagerbestandsmenge für Sie, wenn Bestellungen und Retouren erstellt werden.", + "allowBackordersLabel": "Rückstände zulassen", + "allowBackordersHint": "Wenn diese Option aktiviert ist, können Kunden die Variante auch dann kaufen, wenn keine verfügbare Menge vorhanden ist.", + "toast": { + "levelsBatch": "Lagerbestände aktualisiert.", + "update": "Inventarartikel erfolgreich aktualisiert.", + "updateLevel": "Der Lagerbestand wurde erfolgreich aktualisiert.", + "itemsManageSuccess": "Inventargegenstände wurden erfolgreich aktualisiert." + } + } + }, + "options": { + "header": "Optionen", + "edit": { + "header": "Option bearbeiten", + "successToast": "Option {{title}} wurde erfolgreich aktualisiert." + }, + "create": { + "header": "Option erstellen", + "successToast": "Option {{title}} wurde erfolgreich erstellt." + }, + "deleteWarning": "Sie sind dabei, die Produktoption zu löschen: {{title}}. Diese Aktion kann nicht rückgängig gemacht werden." + }, + "organization": { + "header": "Organisieren", + "edit": { + "header": "Organisation bearbeiten", + "toasts": { + "success": "Die Organisation von {{title}} wurde erfolgreich aktualisiert." + } + } + }, + "toasts": { + "delete": { + "success": { + "header": "Produkt gelöscht", + "description": "{{title}} wurde erfolgreich gelöscht." + }, + "error": { + "header": "Das Produkt konnte nicht gelöscht werden" + } + } + } + }, + "collections": { + "domain": "Sammlungen", + "subtitle": "Organisieren Sie Produkte in Sammlungen.", + "createCollection": "Sammlung erstellen", + "createCollectionHint": "Erstellen Sie eine neue Sammlung, um Ihre Produkte zu organisieren.", + "createSuccess": "Sammlung erfolgreich erstellt.", + "editCollection": "Sammlung bearbeiten", + "handleTooltip": "Das Handle wird verwendet, um auf die Sammlung in Ihrer Storefront zu verweisen. Wenn nicht angegeben, wird das Handle aus dem Sammlungstitel generiert.", + "deleteWarning": "Sie sind dabei, die Sammlung {{title}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeSingleProductWarning": "Sie sind dabei, das Produkt {{title}} aus der Sammlung zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeProductsWarning_one": "Sie sind dabei, {{count}} Produkt aus der Sammlung zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeProductsWarning_other": "Sie sind dabei, {{count}} Produkte aus der Sammlung zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "products": { + "list": { + "noRecordsMessage": "Es sind keine Produkte in der Sammlung vorhanden." + }, + "add": { + "successToast_one": "Das Produkt wurde erfolgreich zur Sammlung hinzugefügt.", + "successToast_other": "Produkte wurden erfolgreich zur Sammlung hinzugefügt." + }, + "remove": { + "successToast_one": "Das Produkt wurde erfolgreich aus der Sammlung entfernt.", + "successToast_other": "Produkte wurden erfolgreich aus der Sammlung entfernt." + } + } + }, + "categories": { + "domain": "Kategorien", + "subtitle": "Organisieren Sie Produkte in Kategorien und verwalten Sie die Rangfolge und Hierarchie dieser Kategorien.", + "create": { + "header": "Kategorie erstellen", + "hint": "Erstellen Sie eine neue Kategorie, um Ihre Produkte zu organisieren.", + "tabs": { + "details": "Einzelheiten", + "organize": "Ranking organisieren" + }, + "successToast": "Kategorie {{name}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Kategorie bearbeiten", + "description": "Bearbeiten Sie die Kategorie, um ihre Details zu aktualisieren.", + "successToast": "Die Kategorie wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, die Kategorie {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Kategorie {{name}} wurde erfolgreich gelöscht." + }, + "products": { + "add": { + "disabledTooltip": "Das Produkt ist bereits in dieser Kategorie.", + "successToast_one": "{{count}} Produkt zur Kategorie hinzugefügt.", + "successToast_other": "{{count}} Produkte zur Kategorie hinzugefügt." + }, + "remove": { + "confirmation_one": "Sie sind dabei, {{count}} Produkt aus der Kategorie zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "confirmation_other": "Sie sind dabei, {{count}} Produkte aus der Kategorie zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast_one": "{{count}} Produkt aus der Kategorie entfernt.", + "successToast_other": "{{count}} Produkte aus der Kategorie entfernt." + }, + "list": { + "noRecordsMessage": "Es gibt keine Produkte in der Kategorie." + } + }, + "organize": { + "header": "Organisieren", + "action": "Ranking bearbeiten" + }, + "fields": { + "visibility": { + "label": "Sichtweite", + "internal": "Intern", + "public": "Öffentlich" + }, + "status": { + "label": "Status", + "active": "Aktiv", + "inactive": "Inaktiv" + }, + "path": { + "label": "Weg", + "tooltip": "Zeigt den vollständigen Pfad der Kategorie an." + }, + "children": { + "label": "Kinder" + }, + "new": { + "label": "Neu" + } + } + }, + "inventory": { + "domain": "Inventar", + "subtitle": "Verwalten Sie Ihre Inventargegenstände", + "reserved": "Reserviert", + "available": "Verfügbar", + "locationLevels": "Standorte", + "associatedVariants": "Zugehörige Varianten", + "manageLocations": "Standorte verwalten", + "deleteWarning": "Sie sind dabei, einen Inventarartikel zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "editItemDetails": "Artikeldetails bearbeiten", + "create": { + "title": "Inventarartikel erstellen", + "details": "Einzelheiten", + "availability": "Verfügbarkeit", + "locations": "Standorte", + "attributes": "Attribute", + "requiresShipping": "Erfordert Versand", + "requiresShippingHint": "Ist für den Lagerartikel ein Versand erforderlich?", + "successToast": "Der Inventarartikel wurde erfolgreich erstellt." + }, + "reservation": { + "header": "Reservierung von {{itemName}}", + "editItemDetails": "Reservierung bearbeiten", + "lineItemId": "Werbebuchungs-ID", + "orderID": "Bestell-ID", + "description": "Beschreibung", + "location": "Standort", + "inStockAtLocation": "An diesem Standort auf Lager", + "availableAtLocation": "Verfügbar an diesem Standort", + "reservedAtLocation": "An diesem Standort reserviert", + "reservedAmount": "Reservebetrag", + "create": "Reservierung erstellen", + "itemToReserve": "Artikel zum Reservieren", + "quantityPlaceholder": "Wie viel möchten Sie reservieren?", + "descriptionPlaceholder": "Um welche Art von Reservierung handelt es sich?", + "successToast": "Die Reservierung wurde erfolgreich erstellt.", + "updateSuccessToast": "Die Reservierung wurde erfolgreich aktualisiert.", + "deleteSuccessToast": "Die Reservierung wurde erfolgreich gelöscht.", + "errors": { + "noAvaliableQuantity": "Der Lagerort hat keine verfügbare Menge.", + "quantityOutOfRange": "Die Mindestmenge beträgt 1 und die Höchstmenge beträgt {{max}}." + } + }, + "toast": { + "updateLocations": "Standorte erfolgreich aktualisiert.", + "updateLevel": "Der Lagerbestand wurde erfolgreich aktualisiert.", + "updateItem": "Inventarartikel erfolgreich aktualisiert." + } + }, + "giftCards": { + "domain": "Geschenkkarten", + "editGiftCard": "Geschenkkarte bearbeiten", + "createGiftCard": "Geschenkkarte erstellen", + "createGiftCardHint": "Erstellen Sie manuell eine Geschenkkarte, die als Zahlungsmethode in Ihrem Shop verwendet werden kann.", + "selectRegionFirst": "Wählen Sie zunächst eine Region aus", + "deleteGiftCardWarning": "Sie sind dabei, die Geschenkkarte {{code}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "balanceHigherThanValue": "Der Saldo darf nicht höher sein als der ursprüngliche Betrag.", + "balanceLowerThanZero": "Der Saldo darf nicht negativ sein.", + "expiryDateHint": "Länder haben unterschiedliche Gesetze bezüglich des Ablaufdatums von Geschenkkarten. Informieren Sie sich unbedingt über die örtlichen Vorschriften, bevor Sie ein Ablaufdatum festlegen.", + "regionHint": "Wenn Sie die Region der Geschenkkarte ändern, ändert sich auch deren Währung, was möglicherweise Auswirkungen auf den Geldwert hat.", + "enabledHint": "Geben Sie an, ob die Geschenkkarte aktiviert oder deaktiviert ist.", + "balance": "Gleichgewicht", + "currentBalance": "Aktueller Kontostand", + "initialBalance": "Anfangssaldo", + "personalMessage": "Persönliche Nachricht", + "recipient": "Empfänger" + }, + "customers": { + "domain": "Kunden", + "list": { + "noRecordsMessage": "Ihre Kunden werden hier angezeigt." + }, + "create": { + "header": "Kunde anlegen", + "hint": "Erstellen Sie einen neuen Kunden und verwalten Sie dessen Daten.", + "successToast": "Der Kunde {{email}} wurde erfolgreich erstellt." + }, + "groups": { + "label": "Kundengruppen", + "remove": "Sind Sie sicher, dass Sie den Kunden aus der Kundengruppe „{{name}}“ entfernen möchten?", + "removeMany": "Sind Sie sicher, dass Sie Kunden aus den folgenden Kundengruppen gewinnen möchten: {{groups}}?", + "alreadyAddedTooltip": "Der Kunde ist bereits in dieser Kundengruppe.", + "list": { + "noRecordsMessage": "Dieser Kunde gehört keiner Gruppe an." + }, + "add": { + "success": "Kunde hinzugefügt zu: {{groups}}.", + "list": { + "noRecordsMessage": "Bitte erstellen Sie zunächst eine Kundengruppe." + } + }, + "removed": { + "success": "Kunde entfernt aus: {{groups}}.", + "list": { + "noRecordsMessage": "Bitte erstellen Sie zunächst eine Kundengruppe." + } + } + }, + "edit": { + "header": "Kunde bearbeiten", + "emailDisabledTooltip": "Die E-Mail-Adresse kann für registrierte Kunden nicht geändert werden.", + "successToast": "Der Kunde {{email}} wurde erfolgreich aktualisiert." + }, + "delete": { + "title": "Kunde löschen", + "description": "Sie sind dabei, den Kunden {{email}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Der Kunde {{email}} wurde erfolgreich gelöscht." + }, + "fields": { + "guest": "Gast", + "registered": "Eingetragen", + "groups": "Gruppen" + }, + "registered": "Eingetragen", + "guest": "Gast", + "hasAccount": "Hat Konto" + }, + "customerGroups": { + "domain": "Kundengruppen", + "subtitle": "Organisieren Sie Kunden in Gruppen. Für Gruppen können unterschiedliche Aktionen und Preise gelten.", + "create": { + "header": "Kundengruppe erstellen", + "hint": "Erstellen Sie eine neue Kundengruppe, um Ihre Kunden zu segmentieren.", + "successToast": "Die Kundengruppe {{name}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Kundengruppe bearbeiten", + "successToast": "Die Kundengruppe {{name}} wurde erfolgreich aktualisiert." + }, + "delete": { + "title": "Kundengruppe löschen", + "description": "Sie sind dabei, die Kundengruppe {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Die Kundengruppe {{name}} wurde erfolgreich gelöscht." + }, + "customers": { + "alreadyAddedTooltip": "Der Kunde wurde bereits zur Gruppe hinzugefügt.", + "add": { + "successToast_one": "Der Kunde wurde erfolgreich zur Gruppe hinzugefügt.", + "successToast_other": "Kunden wurden erfolgreich zur Gruppe hinzugefügt.", + "list": { + "noRecordsMessage": "Erstellen Sie zunächst einen Kunden." + } + }, + "remove": { + "title_one": "Kunde entfernen", + "title_other": "Kunden entfernen", + "description_one": "Sie sind dabei, {{count}} Kunden aus der Kundengruppe zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "description_other": "Sie sind dabei, {{count}} Kunden aus der Kundengruppe zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden." + }, + "list": { + "noRecordsMessage": "Diese Gruppe hat keine Kunden." + } + } + }, + "orders": { + "domain": "Bestellungen", + "claim": "Beanspruchen", + "exchange": "Austausch", + "return": "Zurückkehren", + "cancelWarning": "Sie sind dabei, die Bestellung {{id}} zu stornieren. Diese Aktion kann nicht rückgängig gemacht werden.", + "onDateFromSalesChannel": "{{date}} von {{salesChannel}}", + "list": { + "noRecordsMessage": "Ihre Bestellungen werden hier angezeigt." + }, + "summary": { + "requestReturn": "Rücksendung anfordern", + "allocateItems": "Artikel zuordnen", + "editOrder": "Bestellung bearbeiten", + "editOrderContinue": "Bearbeiten Sie die Bestellung weiter", + "inventoryKit": "Besteht aus {{count}}x Inventargegenständen", + "itemTotal": "Artikelsumme", + "shippingTotal": "Versand insgesamt", + "discountTotal": "Rabatt insgesamt", + "taxTotalIncl": "Steuersumme (inklusive)", + "itemSubtotal": "Zwischensumme des Artikels", + "shippingSubtotal": "Versand-Zwischensumme", + "discountSubtotal": "Rabatt-Zwischensumme", + "taxTotal": "Steuersumme" + }, + "payment": { + "title": "Zahlungen", + "isReadyToBeCaptured": "Die Zahlung <0/> kann erfasst werden.", + "totalPaidByCustomer": "Vom Kunden bezahlter Gesamtbetrag", + "capture": "Zahlung erfassen", + "capture_short": "Erfassen", + "refund": "Erstattung", + "markAsPaid": "Als bezahlt markieren", + "statusLabel": "Zahlungsstatus", + "statusTitle": "Zahlungsstatus", + "status": { + "notPaid": "Nicht bezahlt", + "authorized": "Autorisiert", + "partiallyAuthorized": "Teilweise autorisiert", + "awaiting": "Warten", + "captured": "Gefangen", + "partiallyRefunded": "Teilweise erstattet", + "partiallyCaptured": "Teilweise erfasst", + "refunded": "Erstattet", + "canceled": "Abgesagt", + "requiresAction": "Erfordert Maßnahmen" + }, + "capturePayment": "Die Zahlung von {{amount}} wird erfasst.", + "capturePaymentSuccess": "Die Zahlung von {{amount}} wurde erfolgreich erfasst", + "markAsPaidPayment": "Die Zahlung von {{amount}} wird als bezahlt markiert.", + "markAsPaidPaymentSuccess": "Zahlung von {{amount}} erfolgreich als bezahlt markiert", + "createRefund": "Rückerstattung erstellen", + "refundPaymentSuccess": "Rückerstattung des Betrags {{amount}} erfolgreich", + "createRefundWrongQuantity": "Die Menge sollte eine Zahl zwischen 1 und {{number}} sein.", + "refundAmount": "Rückerstattung von {{ Betrag }}", + "paymentLink": "Zahlungslink für {{ Betrag }} kopieren", + "selectPaymentToRefund": "Wählen Sie die Zahlung zur Rückerstattung aus" + }, + "edits": { + "title": "Bestellung bearbeiten", + "confirm": "Bestätigen Sie Bearbeiten", + "confirmText": "Sie sind dabei, eine Bestellbearbeitung zu bestätigen. Diese Aktion kann nicht rückgängig gemacht werden.", + "cancel": "Bearbeiten abbrechen", + "currentItems": "Aktuelle Artikel", + "currentItemsDescription": "Artikelmenge anpassen oder entfernen.", + "addItemsDescription": "Sie können der Bestellung neue Artikel hinzufügen.", + "addItems": "Elemente hinzufügen", + "amountPaid": "Gezahlter Betrag", + "newTotal": "Neue Summe", + "differenceDue": "Differenz fällig", + "create": "Bestellung bearbeiten", + "currentTotal": "Aktuelle Gesamtsumme", + "noteHint": "Fügen Sie eine interne Notiz für die Bearbeitung hinzu", + "cancelSuccessToast": "Auftragsbearbeitung abgebrochen", + "createSuccessToast": "Anfrage zur Auftragsbearbeitung erstellt", + "activeChangeError": "Für die Bestellung gibt es bereits eine aktive Bestelländerung (Rückgabe, Reklamation, Umtausch usw.). Bitte schließen Sie die Änderung ab oder brechen Sie sie ab, bevor Sie die Bestellung bearbeiten.", + "panel": { + "title": "Auftragsbearbeitung angefordert", + "titlePending": "Auftragsbearbeitung steht aus" + }, + "toast": { + "canceledSuccessfully": "Auftragsbearbeitung abgebrochen", + "confirmedSuccessfully": "Auftragsbearbeitung bestätigt" + }, + "validation": { + "quantityLowerThanFulfillment": "Die Menge kann nicht kleiner oder gleich der erfüllten Menge sein" + } + }, + "returns": { + "create": "Retoure erstellen", + "confirm": "Bestätigen Sie die Rückgabe", + "confirmText": "Sie sind dabei, eine Rücksendung zu bestätigen. Diese Aktion kann nicht rückgängig gemacht werden.", + "inbound": "Eingehend", + "outbound": "Ausgehend", + "sendNotification": "Benachrichtigung senden", + "sendNotificationHint": "Benachrichtigen Sie den Kunden über die Rücksendung.", + "returnTotal": "Gesamtrückgabe", + "inboundTotal": "Eingehende Summe", + "refundAmount": "Rückerstattungsbetrag", + "outstandingAmount": "Ausstehender Betrag", + "reason": "Grund", + "reasonHint": "Wählen Sie aus, warum der Kunde Artikel zurückgeben möchte.", + "note": "Notiz", + "noInventoryLevel": "Kein Lagerbestand", + "noInventoryLevelDesc": "Der ausgewählte Standort verfügt über keinen Lagerbestand für die ausgewählten Artikel. Die Rücksendung kann angefordert werden, kann jedoch erst dann entgegengenommen werden, wenn ein Lagerbestand für den ausgewählten Standort erstellt wurde.", + "noteHint": "Sie können frei tippen, wenn Sie etwas spezifizieren möchten.", + "location": "Standort", + "locationHint": "Wählen Sie den Ort aus, an den Sie die Artikel zurückgeben möchten.", + "inboundShipping": "Rückversand", + "inboundShippingHint": "Wählen Sie die Methode aus, die Sie verwenden möchten.", + "returnableQuantityLabel": "Rückgabemenge", + "refundableAmountLabel": "Rückerstattungsbetrag", + "returnRequestedInfo": "Rückgabe von {{requestedItemsCount}}x Artikeln angefordert", + "returnReceivedInfo": "{{requestedItemsCount}}x Artikel wurden erhalten zurückgegeben", + "itemReceived": "Erhaltene Artikel", + "returnRequested": "Rückgabe erbeten", + "damagedItemReceived": "Beschädigte Artikel erhalten", + "damagedItemsReturned": "{{quantity}}x beschädigte Artikel zurückgegeben", + "activeChangeError": "Für diese Bestellung wird gerade eine aktive Auftragsänderung durchgeführt. Bitte schließen Sie die Änderung zunächst ab oder verwerfen Sie sie.", + "cancel": { + "title": "Rückgabe abbrechen", + "description": "Sind Sie sicher, dass Sie die Rückgabeanfrage stornieren möchten?" + }, + "placeholders": { + "noReturnShippingOptions": { + "title": "Keine Rücksendeoptionen gefunden", + "hint": "Für den Standort wurden keine Rücksendeoptionen erstellt. Sie können eine unter Standort & Versand erstellen." + }, + "outboundShippingOptions": { + "title": "Keine Optionen für den ausgehenden Versand gefunden", + "hint": "Für den Standort wurden keine ausgehenden Versandoptionen erstellt. Sie können eine unter Standort & Versand erstellen." + } + }, + "receive": { + "action": "Artikel erhalten", + "receiveItems": "{{ returnType }} {{ id }}", + "restockAll": "Alle Artikel wieder auffüllen", + "itemsLabel": "Erhaltene Artikel", + "title": "Erhalten Sie Artikel für #{{returnId}}", + "sendNotificationHint": "Benachrichtigen Sie den Kunden über den Erhalt der Rücksendung.", + "inventoryWarning": "Bitte beachten Sie, dass wir die Lagerbestände basierend auf Ihrer Eingabe oben automatisch anpassen.", + "writeOffInputLabel": "Wie viele der Gegenstände sind beschädigt?", + "toast": { + "success": "Rücksendung erfolgreich erhalten.", + "errorLargeValue": "Die Menge ist größer als die angeforderte Artikelmenge.", + "errorNegativeValue": "Die Menge darf kein negativer Wert sein.", + "errorLargeDamagedValue": "Die Menge der beschädigten Artikel + die Menge der unbeschädigten erhaltenen Artikel übersteigt die Gesamtmenge der Artikel in der Rücksendung. Bitte reduzieren Sie die Menge unbeschädigter Artikel." + } + }, + "toast": { + "canceledSuccessfully": "Rücksendung erfolgreich storniert", + "confirmedSuccessfully": "Rücksendung erfolgreich bestätigt" + }, + "panel": { + "title": "Rückkehr eingeleitet", + "description": "Es muss ein offener Rückgabeantrag ausgefüllt werden" + } + }, + "claims": { + "create": "Anspruch erstellen", + "confirm": "Anspruch bestätigen", + "confirmText": "Sie sind dabei, einen Anspruch zu bestätigen. Diese Aktion kann nicht rückgängig gemacht werden.", + "manage": "Anspruch verwalten", + "outbound": "Ausgehend", + "outboundItemAdded": "{{itemsCount}}x durch Anspruch hinzugefügt", + "outboundTotal": "Ausgehender Gesamtwert", + "outboundShipping": "Ausgehender Versand", + "outboundShippingHint": "Wählen Sie die Methode aus, die Sie verwenden möchten.", + "refundAmount": "Geschätzter Unterschied", + "activeChangeError": "Für diese Bestellung liegt eine aktive Bestelländerung vor. Bitte schließen Sie die vorherige Änderung ab oder verwerfen Sie sie.", + "actions": { + "cancelClaim": { + "successToast": "Der Anspruch wurde erfolgreich storniert." + } + }, + "cancel": { + "title": "Anspruch stornieren", + "description": "Sind Sie sicher, dass Sie den Anspruch stornieren möchten?" + }, + "tooltips": { + "onlyReturnShippingOptions": "Diese Liste enthält nur Optionen für den Rückversand." + }, + "toast": { + "canceledSuccessfully": "Anspruch erfolgreich storniert", + "confirmedSuccessfully": "Anspruch erfolgreich bestätigt" + }, + "panel": { + "title": "Klage eingeleitet", + "description": "Es muss ein offener Anspruchsantrag ausgefüllt werden" + } + }, + "exchanges": { + "create": "Exchange erstellen", + "manage": "Exchange verwalten", + "confirm": "Bestätigen Sie den Austausch", + "confirmText": "Sie sind dabei, einen Austausch zu bestätigen. Diese Aktion kann nicht rückgängig gemacht werden.", + "outbound": "Ausgehend", + "outboundItemAdded": "{{itemsCount}}x durch Austausch hinzugefügt", + "outboundTotal": "Ausgehender Gesamtwert", + "outboundShipping": "Ausgehender Versand", + "outboundShippingHint": "Wählen Sie die Methode aus, die Sie verwenden möchten.", + "refundAmount": "Geschätzter Unterschied", + "activeChangeError": "Für diese Bestellung liegt eine aktive Bestelländerung vor. Bitte schließen Sie die vorherige Änderung ab oder verwerfen Sie sie.", + "actions": { + "cancelExchange": { + "successToast": "Der Umtausch wurde erfolgreich abgebrochen." + } + }, + "cancel": { + "title": "Umtausch abbrechen", + "description": "Sind Sie sicher, dass Sie den Umtausch abbrechen möchten?" + }, + "tooltips": { + "onlyReturnShippingOptions": "Diese Liste enthält nur Optionen für den Rückversand." + }, + "toast": { + "canceledSuccessfully": "Der Umtausch wurde erfolgreich abgebrochen", + "confirmedSuccessfully": "Austausch erfolgreich bestätigt" + }, + "panel": { + "title": "Austausch eingeleitet", + "description": "Es muss eine offene Austauschanfrage ausgefüllt werden" + } + }, + "reservations": { + "allocatedLabel": "Zugewiesen", + "notAllocatedLabel": "Nicht zugeordnet" + }, + "allocateItems": { + "action": "Artikel zuordnen", + "title": "Bestellpositionen zuordnen", + "locationDescription": "Wählen Sie den Standort aus, von dem aus Sie zuweisen möchten.", + "itemsToAllocate": "Zuzuordnende Elemente", + "itemsToAllocateDesc": "Wählen Sie die Anzahl der Elemente aus, die Sie zuweisen möchten", + "search": "Elemente suchen", + "consistsOf": "Besteht aus {{num}}x Inventargegenständen", + "requires": "Benötigt {{num}} pro Variante", + "toast": { + "created": "Elemente erfolgreich zugewiesen" + }, + "error": { + "quantityNotAllocated": "Es sind nicht zugeordnete Elemente vorhanden." + } + }, + "shipment": { + "title": "Als Versand markieren", + "trackingNumber": "Tracking-Nummer", + "addTracking": "Tracking-Nummer hinzufügen", + "sendNotification": "Benachrichtigung senden", + "sendNotificationHint": "Benachrichtigen Sie den Kunden über diese Lieferung.", + "toastCreated": "Sendung erfolgreich erstellt." + }, + "fulfillment": { + "cancelWarning": "Sie sind im Begriff, eine Erfüllung zu stornieren. Diese Aktion kann nicht rückgängig gemacht werden.", + "markAsDeliveredWarning": "Sie sind dabei, die Erfüllung als geliefert zu markieren. Diese Aktion kann nicht rückgängig gemacht werden.", + "unfulfilledItems": "Unerfüllte Artikel", + "statusLabel": "Erfüllungsstatus", + "statusTitle": "Erfüllungsstatus", + "fulfillItems": "Artikel erfüllen", + "awaitingFulfillmentBadge": "Warten auf Erfüllung", + "requiresShipping": "Erfordert Versand", + "number": "Erfüllung #{{number}}", + "itemsToFulfill": "Zu erfüllende Gegenstände", + "create": "Schaffen Sie Erfüllung", + "available": "Verfügbar", + "inStock": "Auf Lager", + "markAsShipped": "Als versendet markieren", + "markAsDelivered": "Als geliefert markieren", + "itemsToFulfillDesc": "Wählen Sie die zu erfüllenden Artikel und Mengen aus", + "locationDescription": "Wählen Sie den Standort aus, von dem aus Sie Artikel versenden möchten.", + "sendNotificationHint": "Benachrichtigen Sie Kunden über die erstellte Erfüllung.", + "methodDescription": "Wählen Sie eine andere Versandart als die vom Kunden ausgewählte", + "error": { + "wrongQuantity": "Es steht nur ein Artikel zur Erfüllung zur Verfügung", + "wrongQuantity_other": "Die Menge sollte eine Zahl zwischen 1 und {{number}} sein.", + "noItems": "Keine zu erfüllenden Punkte." + }, + "status": { + "notFulfilled": "Nicht erfüllt", + "partiallyFulfilled": "Teilweise erfüllt", + "fulfilled": "Erfüllt", + "partiallyShipped": "Teilweise versandt", + "shipped": "Ausgeliefert", + "delivered": "Geliefert", + "partiallyDelivered": "Teilweise geliefert", + "partiallyReturned": "Teilweise zurückgegeben", + "returned": "Zurückgegeben", + "canceled": "Abgesagt", + "requiresAction": "Erfordert Maßnahmen" + }, + "toast": { + "created": "Erfüllung erfolgreich erstellt", + "canceled": "Die Erfüllung wurde erfolgreich abgebrochen", + "fulfillmentShipped": "Eine bereits versandte Erfüllung kann nicht storniert werden", + "fulfillmentDelivered": "Die Erfüllung wurde als erfolgreich geliefert markiert" + }, + "trackingLabel": "Verfolgung", + "shippingFromLabel": "Versand ab", + "itemsLabel": "Artikel" + }, + "refund": { + "title": "Rückerstattung erstellen", + "sendNotificationHint": "Benachrichtigen Sie Kunden über die erstellte Rückerstattung.", + "systemPayment": "Systemzahlung", + "systemPaymentDesc": "Eine oder mehrere Ihrer Zahlungen sind eine Systemzahlung. Beachten Sie, dass Medusa für solche Zahlungen keine Einziehungen und Rückerstattungen übernimmt.", + "error": { + "amountToLarge": "Es kann nicht mehr als der ursprüngliche Bestellbetrag erstattet werden.", + "amountNegative": "Der Rückerstattungsbetrag muss eine positive Zahl sein.", + "reasonRequired": "Bitte wählen Sie einen Rückerstattungsgrund aus." + } + }, + "customer": { + "contactLabel": "Kontakt", + "editEmail": "E-Mail bearbeiten", + "transferOwnership": "Eigentum übertragen", + "editBillingAddress": "Rechnungsadresse bearbeiten", + "editShippingAddress": "Lieferadresse bearbeiten" + }, + "activity": { + "header": "Aktivität", + "showMoreActivities_one": "{{count}} weitere Aktivität anzeigen", + "showMoreActivities_other": "{{count}} weitere Aktivitäten anzeigen", + "comment": { + "label": "Kommentar", + "placeholder": "Hinterlassen Sie einen Kommentar", + "addButtonText": "Kommentar hinzufügen", + "deleteButtonText": "Kommentar löschen" + }, + "events": { + "common": { + "toReturn": "Zurückkehren", + "toSend": "Zum Versenden" + }, + "placed": { + "title": "Bestellung aufgegeben", + "fromSalesChannel": "von {{salesChannel}}" + }, + "canceled": { + "title": "Bestellung storniert" + }, + "payment": { + "awaiting": "Warten auf Zahlung", + "captured": "Zahlung erfasst", + "canceled": "Zahlung storniert", + "refunded": "Zahlung zurückerstattet" + }, + "fulfillment": { + "created": "Artikel erfüllt", + "canceled": "Erfüllung abgesagt", + "shipped": "Artikel versendet", + "delivered": "Gelieferte Artikel", + "items_one": "{{count}} Artikel", + "items_other": "{{count}} Artikel" + }, + "return": { + "created": "Rückgabe #{{returnId}} angefordert", + "canceled": "Rückgabe #{{returnId}} abgebrochen", + "received": "Rückgabe #{{returnId}} erhalten", + "items_one": "{{count}} Artikel zurückgegeben", + "items_other": "{{count}} Artikel zurückgegeben" + }, + "note": { + "comment": "Kommentar", + "byLine": "von {{author}}" + }, + "claim": { + "created": "Anspruch #{{claimId}} angefordert", + "canceled": "Anspruch #{{claimId}} storniert", + "itemsInbound": "{{count}} Artikel, der zurückgegeben werden soll", + "itemsOutbound": "{{count}} Artikel zum Senden" + }, + "exchange": { + "created": "Exchange #{{exchangeId}} angefordert", + "canceled": "Austausch #{{exchangeId}} abgebrochen", + "itemsInbound": "{{count}} Artikel, der zurückgegeben werden soll", + "itemsOutbound": "{{count}} Artikel zum Senden" + }, + "edit": { + "requested": "Bestellbearbeitung #{{editId}} angefordert", + "confirmed": "Bestellbearbeitung #{{editId}} bestätigt" + } + } + }, + "fields": { + "displayId": "Anzeige-ID", + "refundableAmount": "Rückerstattungsbetrag", + "returnableQuantity": "Rückgabemenge" + } + }, + "draftOrders": { + "domain": "Befehlsentwürfe", + "deleteWarning": "Sie sind dabei, den Bestellentwurf {{id}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "paymentLinkLabel": "Zahlungslink", + "cartIdLabel": "Warenkorb-ID", + "markAsPaid": { + "label": "Als bezahlt markieren", + "warningTitle": "Als bezahlt markieren", + "warningDescription": "Sie sind dabei, den Bestellentwurf als bezahlt zu markieren. Dieser Vorgang kann nicht rückgängig gemacht werden und eine spätere Einziehung der Zahlung ist nicht mehr möglich." + }, + "status": { + "open": "Offen", + "completed": "Vollendet" + }, + "create": { + "createDraftOrder": "Bestellentwurf erstellen", + "createDraftOrderHint": "Erstellen Sie einen neuen Bestellentwurf, um die Details einer Bestellung zu verwalten, bevor diese aufgegeben wird.", + "chooseRegionHint": "Wählen Sie eine Region", + "existingItemsLabel": "Vorhandene Artikel", + "existingItemsHint": "Fügen Sie vorhandene Produkte zum Bestellentwurf hinzu.", + "customItemsLabel": "Benutzerdefinierte Artikel", + "customItemsHint": "Fügen Sie benutzerdefinierte Artikel zum Bestellentwurf hinzu.", + "addExistingItemsAction": "Vorhandene Elemente hinzufügen", + "addCustomItemAction": "Benutzerdefiniertes Element hinzufügen", + "noCustomItemsAddedLabel": "Es wurden noch keine benutzerdefinierten Artikel hinzugefügt", + "noExistingItemsAddedLabel": "Es wurden noch keine vorhandenen Artikel hinzugefügt", + "chooseRegionTooltip": "Wählen Sie zunächst eine Region aus", + "useExistingCustomerLabel": "Bestandskunden nutzen", + "addShippingMethodsAction": "Versandarten hinzufügen", + "unitPriceOverrideLabel": "Überschreibung des Stückpreises", + "shippingOptionLabel": "Versandoption", + "shippingOptionHint": "Wählen Sie die Versandoption für den Bestellentwurf.", + "shippingPriceOverrideLabel": "Versandpreisüberschreibung", + "shippingPriceOverrideHint": "Überschreiben Sie den Versandpreis für den Bestellentwurf.", + "sendNotificationLabel": "Benachrichtigung senden", + "sendNotificationHint": "Senden Sie eine Benachrichtigung an den Kunden, wenn der Bestellentwurf erstellt wird." + }, + "validation": { + "requiredEmailOrCustomer": "E-Mail oder Kunde ist erforderlich.", + "requiredItems": "Mindestens ein Artikel ist erforderlich.", + "invalidEmail": "E-Mail muss eine gültige E-Mail-Adresse sein." + } + }, + "stockLocations": { + "domain": "Standorte und Versand", + "list": { + "description": "Verwalten Sie die Lagerstandorte und Versandoptionen Ihres Shops." + }, + "create": { + "header": "Lagerort erstellen", + "hint": "Ein Lagerstandort ist ein physischer Standort, an dem Produkte gelagert und versendet werden.", + "successToast": "Der Standort {{name}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Lagerort bearbeiten", + "viewInventory": "Inventar ansehen", + "successToast": "Standort {{name}} wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, den Lagerort {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden." + }, + "fulfillmentProviders": { + "header": "Fulfillment-Anbieter", + "shippingOptionsTooltip": "Dieses Dropdown-Menü enthält nur Anbieter, die für diesen Standort aktiviert sind. Fügen Sie sie dem Standort hinzu, wenn das Dropdown-Menü deaktiviert ist.", + "label": "Angeschlossene Fulfillment-Anbieter", + "connectedTo": "Verbunden mit {{count}} von {{total}} Fulfillment-Anbietern", + "noProviders": "Dieser Lagerort ist mit keinem Versanddienstleister verbunden.", + "action": "Anbieter verbinden", + "successToast": "Die Fulfillment-Anbieter für den Lagerstandort wurden erfolgreich aktualisiert." + }, + "fulfillmentSets": { + "pickup": { + "header": "Abholen" + }, + "shipping": { + "header": "Versand" + }, + "disable": { + "confirmation": "Sind Sie sicher, dass Sie {{name}} deaktivieren möchten? Dadurch werden alle zugehörigen Servicezonen und Versandoptionen gelöscht. Dies kann nicht rückgängig gemacht werden.", + "pickup": "Die Abholung wurde erfolgreich deaktiviert.", + "shipping": "Der Versand wurde erfolgreich deaktiviert." + }, + "enable": { + "pickup": "Die Abholung wurde erfolgreich aktiviert.", + "shipping": "Der Versand wurde erfolgreich aktiviert." + } + }, + "sidebar": { + "header": "Versandkonfiguration", + "shippingProfiles": { + "label": "Versandprofile", + "description": "Gruppieren Sie Produkte nach Versandanforderungen" + } + }, + "salesChannels": { + "header": "Vertriebskanäle", + "label": "Angebundene Vertriebskanäle", + "connectedTo": "Verbunden mit {{count}} von {{total}} Vertriebskanälen", + "noChannels": "Der Standort ist an keine Vertriebskanäle angeschlossen.", + "action": "Vertriebskanäle verbinden", + "successToast": "Vertriebskanäle wurden erfolgreich aktualisiert." + }, + "shippingOptions": { + "create": { + "shipping": { + "header": "Versandoption für {{zone}} erstellen", + "hint": "Erstellen Sie eine neue Versandoption, um zu definieren, wie Produkte von diesem Standort aus versendet werden.", + "label": "Versandoptionen", + "successToast": "Die Versandoption {{name}} wurde erfolgreich erstellt." + }, + "returns": { + "header": "Erstellen Sie eine Rückgabeoption für {{zone}}", + "hint": "Erstellen Sie eine neue Rückgabeoption, um zu definieren, wie Produkte an diesen Standort zurückgegeben werden.", + "label": "Rückgabeoptionen", + "successToast": "Die Rückgabeoption {{name}} wurde erfolgreich erstellt." + }, + "tabs": { + "details": "Einzelheiten", + "prices": "Preise" + }, + "action": "Option erstellen" + }, + "delete": { + "confirmation": "Sie sind dabei, die Versandoption {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Die Versandoption {{name}} wurde erfolgreich gelöscht." + }, + "edit": { + "header": "Versandoption bearbeiten", + "action": "Option bearbeiten", + "successToast": "Die Versandoption {{name}} wurde erfolgreich aktualisiert." + }, + "pricing": { + "action": "Preise bearbeiten" + }, + "fields": { + "count": { + "shipping_one": "{{count}} Versandoption", + "shipping_other": "{{count}} Versandoptionen", + "returns_one": "{{count}} Rückgabeoption", + "returns_other": "{{count}} Rückgabeoptionen" + }, + "priceType": { + "label": "Preisart", + "options": { + "fixed": { + "label": "Behoben", + "hint": "Der Preis der Versandoption ist fest und ändert sich nicht je nach Inhalt der Bestellung." + }, + "calculated": { + "label": "Berechnet", + "hint": "Der Preis der Versandoption wird vom Versanddienstleister beim Bezahlvorgang berechnet." + } + } + }, + "enableInStore": { + "label": "Im Store aktivieren", + "hint": "Ob Kunden diese Option beim Bezahlvorgang nutzen können." + }, + "provider": "Fulfillment-Anbieter", + "profile": "Versandprofil" + } + }, + "serviceZones": { + "create": { + "headerPickup": "Servicezone für Abholung von {{location}} erstellen", + "headerShipping": "Servicezone für den Versand von {{location}} erstellen", + "action": "Servicezone erstellen", + "successToast": "Die Servicezone {{name}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Servicezone bearbeiten", + "successToast": "Die Servicezone {{name}} wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, die Servicezone {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Die Servicezone {{name}} wurde erfolgreich gelöscht." + }, + "manageAreas": { + "header": "Bereiche für {{name}} verwalten", + "action": "Bereiche verwalten", + "label": "Bereiche", + "hint": "Wählen Sie die geografischen Gebiete aus, die von der Servicezone abgedeckt werden.", + "successToast": "Bereiche für {{name}} wurden erfolgreich aktualisiert." + }, + "fields": { + "noRecords": "Es gibt keine Servicezonen, zu denen Versandoptionen hinzugefügt werden können.", + "tip": "Eine Servicezone ist eine Sammlung geografischer Zonen oder Gebiete. Es wird verwendet, um die verfügbaren Versandoptionen auf eine definierte Gruppe von Standorten zu beschränken." + } + } + }, + "shippingProfile": { + "domain": "Versandprofile", + "subtitle": "Gruppieren Sie Produkte mit ähnlichen Versandanforderungen in Profilen.", + "create": { + "header": "Versandprofil erstellen", + "hint": "Erstellen Sie ein neues Versandprofil, um Produkte mit ähnlichen Versandanforderungen zu gruppieren.", + "successToast": "Das Versandprofil {{name}} wurde erfolgreich erstellt." + }, + "delete": { + "title": "Versandprofil löschen", + "description": "Sie sind dabei, das Versandprofil {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Das Versandprofil {{name}} wurde erfolgreich gelöscht." + }, + "tooltip": { + "type": "Geben Sie den Versandprofiltyp ein, zum Beispiel: Schwer, Übergroß, Nur Fracht usw." + } + }, + "taxRegions": { + "domain": "Steuerregionen", + "list": { + "hint": "Verwalten Sie, was Sie Ihren Kunden berechnen, wenn diese in verschiedenen Ländern und Regionen einkaufen." + }, + "delete": { + "confirmation": "Sie sind dabei, eine Steuerregion zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Die Steuerregion wurde erfolgreich gelöscht." + }, + "create": { + "header": "Steuerregion erstellen", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für ein bestimmtes Land zu definieren.", + "errors": { + "rateIsRequired": "Beim Erstellen eines Standardsteuersatzes ist ein Steuersatz erforderlich.", + "nameIsRequired": "Der Name ist beim Erstellen eines Standardsteuersatzes erforderlich." + }, + "successToast": "Die Steuerregion wurde erfolgreich erstellt." + }, + "province": { + "header": "Provinzen", + "create": { + "header": "Erstellen Sie eine Provinzsteuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für eine bestimmte Provinz zu definieren." + } + }, + "state": { + "header": "Staaten", + "create": { + "header": "Erstellen Sie eine staatliche Steuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für einen bestimmten Staat zu definieren." + } + }, + "stateOrTerritory": { + "header": "Staaten oder Gebiete", + "create": { + "header": "Erstellen Sie eine Steuerregion für Bundesstaat/Territorium", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für einen bestimmten Staat/ein bestimmtes Gebiet zu definieren." + } + }, + "county": { + "header": "Landkreise", + "create": { + "header": "Erstellen Sie eine County-Steuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für einen bestimmten Landkreis zu definieren." + } + }, + "region": { + "header": "Regionen", + "create": { + "header": "Region-Steuerregion erstellen", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für eine bestimmte Region zu definieren." + } + }, + "department": { + "header": "Abteilungen", + "create": { + "header": "Erstellen Sie die Steuerregion der Abteilung", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für eine bestimmte Abteilung zu definieren." + } + }, + "territory": { + "header": "Gebiete", + "create": { + "header": "Erstellen Sie eine Gebietssteuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für ein bestimmtes Gebiet zu definieren." + } + }, + "prefecture": { + "header": "Präfekturen", + "create": { + "header": "Erstellen Sie eine Präfektursteuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für eine bestimmte Präfektur zu definieren." + } + }, + "district": { + "header": "Bezirke", + "create": { + "header": "Erstellen Sie eine Bezirkssteuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für einen bestimmten Bezirk zu definieren." + } + }, + "governorate": { + "header": "Gouvernements", + "create": { + "header": "Erstellen Sie eine Steuerregion für ein Gouvernement", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für ein bestimmtes Gouvernement zu definieren." + } + }, + "canton": { + "header": "Kantone", + "create": { + "header": "Erstellen Sie eine Steuerregion für den Kanton", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für einen bestimmten Kanton zu definieren." + } + }, + "emirate": { + "header": "Emirate", + "create": { + "header": "Erstellen Sie eine Emirate-Steuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für ein bestimmtes Emirat zu definieren." + } + }, + "sublevel": { + "header": "Unterebenen", + "create": { + "header": "Erstellen Sie eine untergeordnete Steuerregion", + "hint": "Erstellen Sie eine neue Steuerregion, um Steuersätze für eine bestimmte Unterebene zu definieren." + } + }, + "taxOverrides": { + "header": "Überschreibt", + "create": { + "header": "Überschreibung erstellen", + "hint": "Erstellen Sie einen Steuersatz, der die Standardsteuersätze für ausgewählte Bedingungen überschreibt." + }, + "edit": { + "header": "Überschreibung bearbeiten", + "hint": "Bearbeiten Sie den Steuersatz, der die Standardsteuersätze für ausgewählte Bedingungen überschreibt." + } + }, + "taxRates": { + "create": { + "header": "Steuersatz erstellen", + "hint": "Erstellen Sie einen neuen Steuersatz, um den Steuersatz für eine Region zu definieren.", + "successToast": "Der Steuersatz wurde erfolgreich erstellt." + }, + "edit": { + "header": "Steuersatz bearbeiten", + "hint": "Bearbeiten Sie den Steuersatz, um den Steuersatz für eine Region zu definieren.", + "successToast": "Der Steuersatz wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, den Steuersatz {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Der Steuersatz wurde erfolgreich gelöscht." + } + }, + "fields": { + "isCombinable": { + "label": "Kombinierbar", + "hint": "Ob dieser Steuersatz mit dem Standardsatz aus der Steuerregion kombiniert werden kann.", + "true": "Kombinierbar", + "false": "Nicht kombinierbar" + }, + "defaultTaxRate": { + "label": "Standardsteuersatz", + "tooltip": "Der Standardsteuersatz für diese Region. Ein Beispiel ist der Standard-Mehrwertsteuersatz für ein Land oder eine Region.", + "action": "Erstellen Sie einen Standardsteuersatz" + }, + "taxRate": "Steuersatz", + "taxCode": "Steuerkennzeichen", + "targets": { + "label": "Ziele", + "hint": "Wählen Sie die Ziele aus, für die dieser Steuersatz gelten soll.", + "options": { + "product": "Produkte", + "productCollection": "Produktkollektionen", + "productTag": "Produkt-Tags", + "productType": "Produkttypen", + "customerGroup": "Kundengruppen" + }, + "operators": { + "in": "In", + "on": "An", + "and": "Und" + }, + "placeholders": { + "product": "Nach Produkten suchen", + "productCollection": "Suchen Sie nach Produktkollektionen", + "productTag": "Suchen Sie nach Produkt-Tags", + "productType": "Suchen Sie nach Produkttypen", + "customerGroup": "Suche nach Kundengruppen" + }, + "tags": { + "product": "Produkt", + "productCollection": "Produktkollektion", + "productTag": "Produkt-Tag", + "productType": "Produkttyp", + "customerGroup": "Kundengruppe" + }, + "modal": { + "header": "Ziele hinzufügen" + }, + "values_one": "{{count}} Wert", + "values_other": "{{count}} Werte", + "numberOfTargets_one": "{{count}} Ziel", + "numberOfTargets_other": "{{count}} Ziele", + "additionalValues_one": "und {{count}} mehr Wert", + "additionalValues_other": "und {{count}} weitere Werte", + "action": "Ziel hinzufügen" + }, + "sublevels": { + "labels": { + "province": "Provinz", + "state": "Zustand", + "region": "Region", + "stateOrTerritory": "Staat/Territorium", + "department": "Abteilung", + "county": "County", + "territory": "Gebiet", + "prefecture": "Präfektur", + "district": "Bezirk", + "governorate": "Gouvernement", + "emirate": "Emirat", + "canton": "Kanton", + "sublevel": "Sublevel-Code" + }, + "placeholders": { + "province": "Provinz auswählen", + "state": "Bundesland auswählen", + "region": "Region auswählen", + "stateOrTerritory": "Wählen Sie Bundesland/Gebiet aus", + "department": "Abteilung auswählen", + "county": "Landkreis auswählen", + "territory": "Gebiet auswählen", + "prefecture": "Präfektur auswählen", + "district": "Bezirk auswählen", + "governorate": "Wählen Sie ein Gouvernement aus", + "emirate": "Emirat auswählen", + "canton": "Kanton auswählen" + }, + "tooltips": { + "sublevel": "Geben Sie den ISO 3166-2-Code für die untergeordnete Steuerregion ein.", + "notPartOfCountry": "{{province}} scheint nicht Teil von {{country}} zu sein. Bitte überprüfen Sie noch einmal, ob dies korrekt ist." + }, + "alert": { + "header": "Untergeordnete Regionen sind für diese Steuerregion deaktiviert", + "description": "Untergeordnete Regionen sind für diese Region standardmäßig deaktiviert. Sie können ihnen ermöglichen, untergeordnete Regionen wie Provinzen, Bundesstaaten oder Territorien zu erstellen.", + "action": "Untergeordnete Regionen aktivieren" + } + }, + "noDefaultRate": { + "label": "Keine Standardrate", + "tooltip": "Für diese Steuerregion gibt es keinen Standardsteuersatz. Wenn es einen Standardsatz gibt, z. B. die Mehrwertsteuer eines Landes, fügen Sie ihn bitte dieser Region hinzu." + } + } + }, + "promotions": { + "domain": "Werbeaktionen", + "sections": { + "details": "Aktionsdetails" + }, + "tabs": { + "template": "Typ", + "details": "Einzelheiten", + "campaign": "Kampagne" + }, + "fields": { + "type": "Typ", + "value_type": "Werttyp", + "value": "Wert", + "campaign": "Kampagne", + "method": "Verfahren", + "allocation": "Zuweisung", + "addCondition": "Bedingung hinzufügen", + "clearAll": "Alles löschen", + "amount": { + "tooltip": "Wählen Sie den Währungscode aus, um die Festlegung des Betrags zu ermöglichen" + }, + "conditions": { + "rules": { + "title": "Wer kann diesen Code verwenden?", + "description": "Welcher Kunde darf den Aktionscode nutzen? Der Aktionscode kann von allen Kunden verwendet werden, sofern er nicht geändert wird." + }, + "target-rules": { + "title": "Auf welche Artikel gilt die Aktion?", + "description": "Die Aktion gilt für Artikel, die die folgenden Bedingungen erfüllen." + }, + "buy-rules": { + "title": "Was muss im Warenkorb sein, um die Aktion freizuschalten?", + "description": "Wenn diese Bedingungen zutreffen, aktivieren wir die Aktion für die Zielartikel." + } + } + }, + "tooltips": { + "campaignType": "Der Währungscode muss in der Aktion ausgewählt werden, um ein Ausgabenbudget festzulegen." + }, + "errors": { + "requiredField": "Erforderliches Feld", + "promotionTabError": "Beheben Sie Fehler im Tab „Werbung“, bevor Sie fortfahren" + }, + "toasts": { + "promotionCreateSuccess": "Aktion ({{code}}) wurde erfolgreich erstellt." + }, + "create": {}, + "edit": { + "title": "Aktionsdetails bearbeiten", + "rules": { + "title": "Nutzungsbedingungen bearbeiten" + }, + "target-rules": { + "title": "Artikelbedingungen bearbeiten" + }, + "buy-rules": { + "title": "Kaufregeln bearbeiten" + } + }, + "campaign": { + "header": "Kampagne", + "edit": { + "header": "Kampagne bearbeiten", + "successToast": "Die Kampagne der Aktion wurde erfolgreich aktualisiert." + }, + "actions": { + "goToCampaign": "Gehen Sie zur Kampagne" + } + }, + "campaign_currency": { + "tooltip": "Dies ist die Währung der Aktion. Ändern Sie es auf der Registerkarte „Details“." + }, + "form": { + "required": "Erforderlich", + "and": "UND", + "selectAttribute": "Wählen Sie Attribut aus", + "campaign": { + "existing": { + "title": "Bestehende Kampagne", + "description": "Fügen Sie Werbung zu einer vorhandenen Kampagne hinzu.", + "placeholder": { + "title": "Keine vorhandenen Kampagnen", + "desc": "Sie können eine erstellen, um mehrere Werbeaktionen zu verfolgen und Budgetgrenzen festzulegen." + } + }, + "new": { + "title": "Neue Kampagne", + "description": "Erstellen Sie eine neue Kampagne für diese Aktion." + }, + "none": { + "title": "Ohne Kampagne", + "description": "Fahren Sie fort, ohne Werbung mit Kampagne zu verknüpfen" + } + }, + "status": { + "title": "Status" + }, + "method": { + "label": "Verfahren", + "code": { + "title": "Aktionscode", + "description": "Kunden müssen diesen Code beim Bezahlvorgang eingeben" + }, + "automatic": { + "title": "Automatisch", + "description": "Kunden werden diese Aktion an der Kasse sehen" + } + }, + "max_quantity": { + "title": "Maximale Menge", + "description": "Maximale Anzahl an Artikeln, für die diese Aktion gilt." + }, + "type": { + "standard": { + "title": "Standard", + "description": "Eine Standardaktion" + }, + "buyget": { + "title": "Kaufen Get", + "description": "Kaufen Sie X und erhalten Sie Y-Aktion" + } + }, + "allocation": { + "each": { + "title": "Jede", + "description": "Wendet den Wert auf jedes Element an" + }, + "across": { + "title": "Über", + "description": "Wendet den Wert auf alle Elemente an" + } + }, + "code": { + "title": "Code", + "description": "Der Code, den Ihre Kunden beim Bezahlvorgang eingeben." + }, + "value": { + "title": "Werbewert" + }, + "value_type": { + "fixed": { + "title": "Werbewert", + "description": "Der zu rabattierende Betrag. z.B. 100" + }, + "percentage": { + "title": "Werbewert", + "description": "Der Prozentsatz, um den der Betrag abgezinst wird. z.B. 8 %" + } + } + }, + "deleteWarning": "Sie sind dabei, die Aktion {{code}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "createPromotionTitle": "Werbeaktion erstellen", + "type": "Werbetyp", + "conditions": { + "add": "Bedingung hinzufügen", + "list": { + "noRecordsMessage": "Fügen Sie eine Bedingung hinzu, um einzuschränken, für welche Artikel die Aktion gilt." + } + } + }, + "campaigns": { + "domain": "Kampagnen", + "details": "Details zur Kampagne", + "status": { + "active": "Aktiv", + "expired": "Abgelaufen", + "scheduled": "Geplant" + }, + "delete": { + "title": "Bist du sicher?", + "description": "Sie sind dabei, die Kampagne „{{name}}“ zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Die Kampagne „{{name}}“ wurde erfolgreich erstellt." + }, + "edit": { + "header": "Kampagne bearbeiten", + "description": "Bearbeiten Sie die Details der Kampagne.", + "successToast": "Kampagne „{{name}}“ wurde erfolgreich aktualisiert." + }, + "configuration": { + "header": "Konfiguration", + "edit": { + "header": "Kampagnenkonfiguration bearbeiten", + "description": "Bearbeiten Sie die Konfiguration der Kampagne.", + "successToast": "Die Kampagnenkonfiguration wurde erfolgreich aktualisiert." + } + }, + "create": { + "title": "Kampagne erstellen", + "description": "Erstellen Sie eine Werbekampagne.", + "hint": "Erstellen Sie eine Werbekampagne.", + "header": "Kampagne erstellen", + "successToast": "Die Kampagne „{{name}}“ wurde erfolgreich erstellt." + }, + "fields": { + "name": "Name", + "identifier": "Kennung", + "start_date": "Startdatum", + "end_date": "Enddatum", + "total_spend": "Budget ausgegeben", + "total_used": "Verwendetes Budget", + "budget_limit": "Budgetlimit", + "campaign_id": { + "hint": "In dieser Liste werden nur Kampagnen mit demselben Währungscode wie die Aktion angezeigt." + } + }, + "budget": { + "create": { + "hint": "Erstellen Sie ein Budget für die Kampagne.", + "header": "Kampagnenbudget" + }, + "details": "Kampagnenbudget", + "fields": { + "type": "Typ", + "currency": "Währung", + "limit": "Limit", + "used": "Gebraucht" + }, + "type": { + "spend": { + "title": "Ausgeben", + "description": "Legen Sie ein Limit für den gesamten rabattierten Betrag aller Aktionsnutzungen fest." + }, + "usage": { + "title": "Verwendung", + "description": "Legen Sie ein Limit fest, wie oft die Aktion genutzt werden kann." + } + }, + "edit": { + "header": "Kampagnenbudget bearbeiten" + } + }, + "promotions": { + "remove": { + "title": "Werbung aus der Kampagne entfernen", + "description": "Sie sind dabei, {{count}} Werbeaktionen aus der Kampagne zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden." + }, + "alreadyAdded": "Diese Aktion wurde bereits zur Kampagne hinzugefügt.", + "alreadyAddedDiffCampaign": "Diese Aktion wurde bereits einer anderen Kampagne ({{name}}) hinzugefügt.", + "currencyMismatch": "Die Währung der Werbeaktion und der Kampagne stimmt nicht überein", + "toast": { + "success": "{{count}} Werbeaktion(en) erfolgreich zur Kampagne hinzugefügt" + }, + "add": { + "list": { + "noRecordsMessage": "Erstellen Sie zunächst eine Werbeaktion." + } + }, + "list": { + "noRecordsMessage": "In der Kampagne gibt es keine Werbeaktionen." + } + }, + "deleteCampaignWarning": "Sie sind dabei, die Kampagne {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "totalSpend": "<0>{{amount}} <1>{{currency}}" + }, + "priceLists": { + "domain": "Preislisten", + "subtitle": "Erstellen Sie Sonderangebote oder überschreiben Sie Preise für bestimmte Konditionen.", + "delete": { + "confirmation": "Sie sind dabei, die Preisliste {{title}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Die Preisliste {{title}} wurde erfolgreich gelöscht." + }, + "create": { + "header": "Preisliste erstellen", + "subheader": "Erstellen Sie eine neue Preisliste, um die Preise Ihrer Produkte zu verwalten.", + "tabs": { + "details": "Einzelheiten", + "products": "Produkte", + "prices": "Preise" + }, + "successToast": "Die Preisliste {{title}} wurde erfolgreich erstellt.", + "products": { + "list": { + "noRecordsMessage": "Erstellen Sie zuerst ein Produkt." + } + } + }, + "edit": { + "header": "Preisliste bearbeiten", + "successToast": "Die Preisliste {{title}} wurde erfolgreich aktualisiert." + }, + "configuration": { + "header": "Konfiguration", + "edit": { + "header": "Bearbeiten Sie die Preislistenkonfiguration", + "description": "Bearbeiten Sie die Konfiguration der Preisliste.", + "successToast": "Die Preislistenkonfiguration wurde erfolgreich aktualisiert." + } + }, + "products": { + "header": "Produkte", + "actions": { + "addProducts": "Produkte hinzufügen", + "editPrices": "Preise bearbeiten" + }, + "delete": { + "confirmation_one": "Sie sind dabei, die Preise für {{count}} Produkt in der Preisliste zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "confirmation_other": "Sie sind dabei, die Preise für {{count}} Produkte in der Preisliste zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast_one": "Preise für {{count}} Produkt erfolgreich gelöscht.", + "successToast_other": "Preise für {{count}} Produkte erfolgreich gelöscht." + }, + "add": { + "successToast": "Die Preise wurden erfolgreich zur Preisliste hinzugefügt." + }, + "edit": { + "successToast": "Die Preise wurden erfolgreich aktualisiert." + } + }, + "fields": { + "priceOverrides": { + "label": "Preisüberschreibungen", + "header": "Preisüberschreibungen" + }, + "status": { + "label": "Status", + "options": { + "active": "Aktiv", + "draft": "Entwurf", + "expired": "Abgelaufen", + "scheduled": "Geplant" + } + }, + "type": { + "label": "Typ", + "hint": "Wählen Sie die Art der Preisliste, die Sie erstellen möchten.", + "options": { + "sale": { + "label": "Verkauf", + "description": "Verkaufspreise sind vorübergehende Preisänderungen für Produkte." + }, + "override": { + "label": "Überschreiben", + "description": "Overrides werden in der Regel verwendet, um kundenspezifische Preise zu erstellen." + } + } + }, + "startsAt": { + "label": "Preisliste hat ein Startdatum?", + "hint": "Planen Sie die zukünftige Aktivierung der Preisliste." + }, + "endsAt": { + "label": "Preisliste hat ein Ablaufdatum?", + "hint": "Planen Sie, die Preisliste in Zukunft zu deaktivieren." + }, + "customerAvailability": { + "header": "Wählen Sie Kundengruppen", + "label": "Kundenverfügbarkeit", + "hint": "Wählen Sie aus, für welche Kundengruppen die Preisliste gelten soll.", + "placeholder": "Suche nach Kundengruppen", + "attribute": "Kundengruppen" + } + } + }, + "profile": { + "domain": "Profil", + "manageYourProfileDetails": "Verwalten Sie Ihre Profildetails.", + "fields": { + "languageLabel": "Sprache", + "usageInsightsLabel": "Nutzungseinblicke" + }, + "edit": { + "header": "Profil bearbeiten", + "languageHint": "Die Sprache, die Sie im Admin-Dashboard verwenden möchten. Die Sprache Ihres Shops ändert sich dadurch nicht.", + "languagePlaceholder": "Sprache auswählen", + "usageInsightsHint": "Teilen Sie Nutzungseinblicke und helfen Sie uns, Medusa zu verbessern. Weitere Informationen darüber, was wir sammeln und wie wir es verwenden, finden Sie in unserer <0>Dokumentation." + }, + "toast": { + "edit": "Profiländerungen gespeichert" + } + }, + "users": { + "domain": "Benutzer", + "editUser": "Benutzer bearbeiten", + "inviteUser": "Benutzer einladen", + "inviteUserHint": "Laden Sie einen neuen Benutzer in Ihren Shop ein.", + "sendInvite": "Einladung senden", + "pendingInvites": "Ausstehende Einladungen", + "deleteInviteWarning": "Sie sind dabei, die Einladung für {{email}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "resendInvite": "Einladung erneut senden", + "copyInviteLink": "Einladungslink kopieren", + "expiredOnDate": "Abgelaufen am {{date}}", + "validFromUntil": "Gültig von <0>{{von}} - <1>{{bis}}", + "acceptedOnDate": "Akzeptiert am {{date}}", + "inviteStatus": { + "accepted": "Akzeptiert", + "pending": "Ausstehend", + "expired": "Abgelaufen" + }, + "roles": { + "admin": "Admin", + "developer": "Entwickler", + "member": "Mitglied" + }, + "deleteUserWarning": "Sie sind dabei, den Benutzer {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "invite": "Einladen" + }, + "store": { + "domain": "Speichern", + "manageYourStoresDetails": "Verwalten Sie die Details Ihres Shops", + "editStore": "Shop bearbeiten", + "defaultCurrency": "Standardwährung", + "defaultRegion": "Standardregion", + "swapLinkTemplate": "Linkvorlage austauschen", + "paymentLinkTemplate": "Vorlage für einen Zahlungslink", + "inviteLinkTemplate": "Vorlage für einen Einladungslink", + "currencies": "Währungen", + "addCurrencies": "Währungen hinzufügen", + "enableTaxInclusivePricing": "Aktivieren Sie die Preisgestaltung inklusive Steuern", + "disableTaxInclusivePricing": "Deaktivieren Sie die Preisgestaltung inklusive Steuern", + "removeCurrencyWarning_one": "Sie sind dabei, {{count}} Währung aus Ihrem Shop zu entfernen. Stellen Sie sicher, dass Sie alle Preise in der Währung entfernt haben, bevor Sie fortfahren.", + "removeCurrencyWarning_other": "Sie sind dabei, {{count}} Währungen aus Ihrem Shop zu entfernen. Stellen Sie sicher, dass Sie alle Preise unter Verwendung der Währungen entfernt haben, bevor Sie fortfahren.", + "currencyAlreadyAdded": "Die Währung wurde Ihrem Shop bereits hinzugefügt.", + "edit": { + "header": "Shop bearbeiten" + }, + "toast": { + "update": "Shop erfolgreich aktualisiert", + "currenciesUpdated": "Währungen erfolgreich aktualisiert", + "currenciesRemoved": "Währungen wurden erfolgreich aus dem Shop entfernt", + "updatedTaxInclusivitySuccessfully": "Die Preise inklusive Steuern wurden erfolgreich aktualisiert" + } + }, + "regions": { + "domain": "Regionen", + "subtitle": "Eine Region ist ein Gebiet, in dem Sie Produkte verkaufen. Sie kann mehrere Länder umfassen und hat unterschiedliche Steuersätze, Anbieter und Währungen.", + "createRegion": "Region erstellen", + "createRegionHint": "Verwalten Sie Steuersätze und Anbieter für eine Reihe von Ländern.", + "addCountries": "Länder hinzufügen", + "editRegion": "Region bearbeiten", + "countriesHint": "Fügen Sie die in dieser Region enthaltenen Länder hinzu.", + "deleteRegionWarning": "Sie sind dabei, die Region {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeCountriesWarning_one": "Sie sind dabei, {{count}} Land aus der Region zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeCountriesWarning_other": "Sie sind dabei, {{count}} Länder aus der Region zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeCountryWarning": "Sie sind dabei, das Land {{name}} aus der Region zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "automaticTaxesHint": "Wenn diese Option aktiviert ist, werden die Steuern beim Bezahlvorgang nur auf Grundlage der Lieferadresse berechnet.", + "taxInclusiveHint": "Wenn diese Option aktiviert ist, verstehen sich die Preise in der Region inklusive Steuern.", + "providersHint": "Fügen Sie hinzu, welche Zahlungsanbieter in dieser Region verfügbar sind.", + "shippingOptions": "Versandoptionen", + "deleteShippingOptionWarning": "Sie sind dabei, die Versandoption {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "return": "Zurückkehren", + "outbound": "Ausgehend", + "priceType": "Preisart", + "flatRate": "Pauschale", + "calculated": "Berechnet", + "list": { + "noRecordsMessage": "Erstellen Sie eine Region für die Gebiete, in denen Sie verkaufen." + }, + "toast": { + "delete": "Region erfolgreich gelöscht", + "edit": "Regionsbearbeitung gespeichert", + "create": "Region erfolgreich erstellt", + "countries": "Die Länder der Region wurden erfolgreich aktualisiert" + }, + "shippingOption": { + "createShippingOption": "Versandoption erstellen", + "createShippingOptionHint": "Erstellen Sie eine neue Versandoption für die Region.", + "editShippingOption": "Versandoption bearbeiten", + "fulfillmentMethod": "Erfüllungsmethode", + "type": { + "outbound": "Ausgehend", + "outboundHint": "Verwenden Sie diese Option, wenn Sie eine Versandoption für den Versand von Produkten an den Kunden erstellen.", + "return": "Zurückkehren", + "returnHint": "Verwenden Sie diese Option, wenn Sie eine Versandoption erstellen, damit der Kunde Produkte an Sie zurücksenden kann." + }, + "priceType": { + "label": "Preisart", + "flatRate": "Pauschale", + "calculated": "Berechnet" + }, + "availability": { + "adminOnly": "Nur Administrator", + "adminOnlyHint": "Wenn diese Option aktiviert ist, ist die Versandoption nur im Admin-Dashboard und nicht im Storefront verfügbar." + }, + "taxInclusiveHint": "Wenn diese Option aktiviert ist, versteht sich der Preis der Versandoption inklusive Steuern.", + "requirements": { + "label": "Anforderungen", + "hint": "Geben Sie die Anforderungen für die Versandoption an." + } + } + }, + "taxes": { + "domain": "Steuerregionen", + "domainDescription": "Verwalten Sie Ihre Steuerregion", + "countries": { + "taxCountriesHint": "Die Steuereinstellungen gelten für die aufgeführten Länder." + }, + "settings": { + "editTaxSettings": "Bearbeiten Sie die Steuereinstellungen", + "taxProviderLabel": "Steueranbieter", + "systemTaxProviderLabel": "Systemsteueranbieter", + "calculateTaxesAutomaticallyLabel": "Steuern automatisch berechnen", + "calculateTaxesAutomaticallyHint": "Wenn diese Option aktiviert ist, werden die Steuersätze automatisch berechnet und auf Warenkörbe angewendet. Wenn die Funktion deaktiviert ist, müssen die Steuern an der Kasse manuell berechnet werden. Manuelle Steuern werden für die Verwendung mit externen Steueranbietern empfohlen.", + "applyTaxesOnGiftCardsLabel": "Erheben Sie Steuern auf Geschenkkarten", + "applyTaxesOnGiftCardsHint": "Wenn diese Option aktiviert ist, werden beim Bezahlvorgang Steuern auf Geschenkkarten erhoben. In einigen Ländern schreiben die Steuervorschriften vor, dass beim Kauf von Geschenkkarten Steuern erhoben werden müssen.", + "defaultTaxRateLabel": "Standardsteuersatz", + "defaultTaxCodeLabel": "Standardsteuercode" + }, + "defaultRate": { + "sectionTitle": "Standardsteuersatz" + }, + "taxRate": { + "sectionTitle": "Steuersätze", + "createTaxRate": "Steuersatz erstellen", + "createTaxRateHint": "Erstellen Sie einen neuen Steuersatz für die Region.", + "deleteRateDescription": "Sie sind dabei, den Steuersatz {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "editTaxRate": "Steuersatz bearbeiten", + "editRateAction": "Tarif bearbeiten", + "editOverridesAction": "Überschreibungen bearbeiten", + "editOverridesTitle": "Steuersatzüberschreibungen bearbeiten", + "editOverridesHint": "Geben Sie die Überschreibungen für den Steuersatz an.", + "deleteTaxRateWarning": "Sie sind dabei, den Steuersatz {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "productOverridesLabel": "Produktüberschreibungen", + "productOverridesHint": "Geben Sie die Produktüberschreibungen für den Steuersatz an.", + "addProductOverridesAction": "Fügen Sie Produktüberschreibungen hinzu", + "productTypeOverridesLabel": "Produkttypüberschreibungen", + "productTypeOverridesHint": "Geben Sie die Produkttypüberschreibungen für den Steuersatz an.", + "addProductTypeOverridesAction": "Fügen Sie Produkttypüberschreibungen hinzu", + "shippingOptionOverridesLabel": "Die Versandoption hat Vorrang", + "shippingOptionOverridesHint": "Geben Sie die Versandoption an, die den Steuersatz überschreibt.", + "addShippingOptionOverridesAction": "Versandoptionsüberschreibungen hinzufügen", + "productOverridesHeader": "Produkte", + "productTypeOverridesHeader": "Produkttypen", + "shippingOptionOverridesHeader": "Versandoptionen" + } + }, + "locations": { + "domain": "Standorte", + "editLocation": "Standort bearbeiten", + "addSalesChannels": "Vertriebskanäle hinzufügen", + "noLocationsFound": "Keine Standorte gefunden", + "selectLocations": "Wählen Sie Standorte aus, an denen der Artikel vorrätig ist.", + "deleteLocationWarning": "Sie sind dabei, den Standort {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "removeSalesChannelsWarning_one": "Sie sind dabei, {{count}} Vertriebskanal vom Standort zu entfernen.", + "removeSalesChannelsWarning_other": "Sie sind dabei, {{count}} Vertriebskanäle vom Standort zu entfernen.", + "toast": { + "create": "Standort erfolgreich erstellt", + "update": "Der Standort wurde erfolgreich aktualisiert", + "removeChannel": "Vertriebskanal erfolgreich entfernt" + } + }, + "reservations": { + "domain": "Reservierungen", + "subtitle": "Verwalten Sie die reservierte Menge an Lagerartikeln.", + "deleteWarning": "Sie sind dabei, eine Reservierung zu löschen. Diese Aktion kann nicht rückgängig gemacht werden." + }, + "salesChannels": { + "domain": "Vertriebskanäle", + "subtitle": "Verwalten Sie die Online- und Offline-Kanäle, über die Sie Produkte verkaufen.", + "createSalesChannel": "Erstellen Sie einen Vertriebskanal", + "createSalesChannelHint": "Erstellen Sie einen neuen Vertriebskanal, über den Sie Ihre Produkte verkaufen können.", + "enabledHint": "Geben Sie an, ob der Vertriebskanal aktiviert ist.", + "removeProductsWarning_one": "Sie sind dabei, {{count}} Produkt aus {{sales_channel}} zu entfernen.", + "removeProductsWarning_other": "Sie sind dabei, {{count}} Produkte aus {{sales_channel}} zu entfernen.", + "addProducts": "Produkte hinzufügen", + "editSalesChannel": "Vertriebskanal bearbeiten", + "productAlreadyAdded": "Das Produkt wurde bereits zum Vertriebskanal hinzugefügt.", + "deleteSalesChannelWarning": "Sie sind dabei, den Vertriebskanal {{name}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "toast": { + "create": "Vertriebskanal erfolgreich erstellt", + "update": "Vertriebskanal erfolgreich aktualisiert", + "delete": "Vertriebskanal erfolgreich gelöscht" + }, + "products": { + "list": { + "noRecordsMessage": "Es sind keine Produkte im Vertriebskanal vorhanden." + }, + "add": { + "list": { + "noRecordsMessage": "Erstellen Sie zuerst ein Produkt." + } + } + } + }, + "apiKeyManagement": { + "domain": { + "publishable": "Veröffentlichbare API-Schlüssel", + "secret": "Geheime API-Schlüssel" + }, + "subtitle": { + "publishable": "Verwalten Sie in der Storefront verwendete API-Schlüssel, um den Umfang der Anfragen auf bestimmte Vertriebskanäle zu beschränken.", + "secret": "Verwalten Sie API-Schlüssel, die zur Authentifizierung von Administratorbenutzern in Administratoranwendungen verwendet werden." + }, + "status": { + "active": "Aktiv", + "revoked": "Widerrufen" + }, + "type": { + "publishable": "Veröffentlichbar", + "secret": "Geheimnis" + }, + "create": { + "createPublishableHeader": "Erstellen Sie einen veröffentlichbaren API-Schlüssel", + "createPublishableHint": "Erstellen Sie einen neuen veröffentlichbaren API-Schlüssel, um den Umfang der Anfragen auf bestimmte Vertriebskanäle zu beschränken.", + "createSecretHeader": "Erstellen Sie einen geheimen API-Schlüssel", + "createSecretHint": "Erstellen Sie einen neuen geheimen API-Schlüssel, um als authentifizierter Admin-Benutzer auf die Medusa-API zuzugreifen.", + "secretKeyCreatedHeader": "Geheimer Schlüssel erstellt", + "secretKeyCreatedHint": "Ihr neuer geheimer Schlüssel wurde generiert. Jetzt kopieren und sicher aufbewahren. Dies ist das einzige Mal, dass es angezeigt wird.", + "copySecretTokenSuccess": "Der geheime Schlüssel wurde in die Zwischenablage kopiert.", + "copySecretTokenFailure": "Der geheime Schlüssel konnte nicht in die Zwischenablage kopiert werden.", + "successToast": "Der API-Schlüssel wurde erfolgreich erstellt." + }, + "edit": { + "header": "API-Schlüssel bearbeiten", + "description": "Bearbeiten Sie den Titel des API-Schlüssels.", + "successToast": "Der API-Schlüssel {{title}} wurde erfolgreich aktualisiert." + }, + "salesChannels": { + "title": "Vertriebskanäle hinzufügen", + "description": "Fügen Sie die Vertriebskanäle hinzu, auf die der API-Schlüssel beschränkt sein soll.", + "successToast_one": "{{count}} Vertriebskanal wurde erfolgreich zum API-Schlüssel hinzugefügt.", + "successToast_other": "{{count}} Vertriebskanäle wurden erfolgreich zum API-Schlüssel hinzugefügt.", + "alreadyAddedTooltip": "Der Vertriebskanal wurde bereits zum API-Schlüssel hinzugefügt.", + "list": { + "noRecordsMessage": "Im Geltungsbereich des veröffentlichbaren API-Schlüssels gibt es keine Vertriebskanäle." + } + }, + "delete": { + "warning": "Sie sind dabei, den API-Schlüssel {{title}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Der API-Schlüssel {{title}} wurde erfolgreich gelöscht." + }, + "revoke": { + "warning": "Sie sind dabei, den API-Schlüssel {{title}} zu widerrufen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Der API-Schlüssel {{title}} wurde erfolgreich widerrufen." + }, + "addSalesChannels": { + "list": { + "noRecordsMessage": "Erstellen Sie zunächst einen Vertriebskanal." + } + }, + "removeSalesChannel": { + "warning": "Sie sind dabei, den Vertriebskanal {{name}} aus dem API-Schlüssel zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "warningBatch_one": "Sie sind dabei, {{count}} Vertriebskanal aus dem API-Schlüssel zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "warningBatch_other": "Sie sind dabei, {{count}} Vertriebskanäle aus dem API-Schlüssel zu entfernen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Der Vertriebskanal wurde erfolgreich aus dem API-Schlüssel entfernt.", + "successToastBatch_one": "{{count}} Vertriebskanal wurde erfolgreich aus dem API-Schlüssel entfernt.", + "successToastBatch_other": "{{count}} Vertriebskanäle wurden erfolgreich aus dem API-Schlüssel entfernt." + }, + "actions": { + "revoke": "API-Schlüssel widerrufen", + "copy": "API-Schlüssel kopieren", + "copySuccessToast": "Der API-Schlüssel wurde in die Zwischenablage kopiert." + }, + "table": { + "lastUsedAtHeader": "Zuletzt verwendet um", + "createdAtHeader": "Widerrufen am" + }, + "fields": { + "lastUsedAtLabel": "Zuletzt verwendet um", + "revokedByLabel": "Widerrufen durch", + "revokedAtLabel": "Widerrufen am", + "createdByLabel": "Erstellt von" + } + }, + "returnReasons": { + "domain": "Rückgabegründe", + "subtitle": "Verwalten Sie die Gründe für zurückgegebene Artikel.", + "calloutHint": "Verwalten Sie die Gründe für die Kategorisierung von Retouren.", + "editReason": "Retourengrund bearbeiten", + "create": { + "header": "Rückgabegrund hinzufügen", + "subtitle": "Geben Sie die häufigsten Gründe für Rücksendungen an.", + "hint": "Erstellen Sie einen neuen Retourengrund, um Retouren zu kategorisieren.", + "successToast": "Rückgabegrund {{label}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Retourengrund bearbeiten", + "subtitle": "Bearbeiten Sie den Wert des Rückgabegrunds.", + "successToast": "Rückgabegrund {{label}} wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, den Rückgabegrund {{label}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Rückgabegrund {{label}} wurde erfolgreich gelöscht." + }, + "fields": { + "value": { + "label": "Wert", + "placeholder": "falsche_Größe", + "tooltip": "Der Wert sollte eine eindeutige Kennung für den Rückgabegrund sein." + }, + "label": { + "label": "Etikett", + "placeholder": "Falsche Größe" + }, + "description": { + "label": "Beschreibung", + "placeholder": "Der Kunde hat die falsche Größe erhalten" + } + } + }, + "login": { + "forgotPassword": "Passwort vergessen? - <0>Zurücksetzen", + "title": "Willkommen bei Medusa", + "hint": "Melden Sie sich an, um auf den Kontobereich zuzugreifen" + }, + "invite": { + "title": "Willkommen bei Medusa", + "hint": "Erstellen Sie unten Ihr Konto", + "backToLogin": "Zurück zum Login", + "createAccount": "Benutzerkonto erstellen", + "alreadyHaveAccount": "Sie haben bereits ein Konto? - <0>Anmelden", + "emailTooltip": "Ihre E-Mail-Adresse kann nicht geändert werden. Wenn Sie eine andere E-Mail-Adresse verwenden möchten, muss eine neue Einladung gesendet werden.", + "invalidInvite": "Die Einladung ist ungültig oder abgelaufen.", + "successTitle": "Ihr Konto wurde registriert", + "successHint": "Beginnen Sie sofort mit Medusa Admin.", + "successAction": "Starten Sie Medusa Admin", + "invalidTokenTitle": "Ihr Einladungstoken ist ungültig", + "invalidTokenHint": "Versuchen Sie, einen neuen Einladungslink anzufordern.", + "passwordMismatch": "Passwörter stimmen nicht überein", + "toast": { + "accepted": "Einladung erfolgreich angenommen" + } + }, + "resetPassword": { + "title": "Passwort zurücksetzen", + "hint": "Geben Sie unten Ihre E-Mail-Adresse ein und wir senden Ihnen Anweisungen zum Zurücksetzen Ihres Passworts.", + "email": "E-Mail", + "sendResetInstructions": "Anweisungen zum Zurücksetzen senden", + "backToLogin": "<0>Zurück zur Anmeldung", + "newPasswordHint": "Wählen Sie unten ein neues Passwort.", + "invalidTokenTitle": "Ihr Reset-Token ist ungültig", + "invalidTokenHint": "Versuchen Sie, einen neuen Link zum Zurücksetzen anzufordern.", + "expiredTokenTitle": "Ihr Reset-Token ist abgelaufen", + "goToResetPassword": "Gehen Sie zu Passwort zurücksetzen", + "resetPassword": "Passwort zurücksetzen", + "newPassword": "Neues Passwort", + "repeatNewPassword": "Neues Passwort wiederholen", + "tokenExpiresIn": "Token läuft in <0>{{time}} Minuten ab", + "successfulRequestTitle": "Ihnen wurde erfolgreich eine E-Mail gesendet", + "successfulRequest": "Wir haben Ihnen eine E-Mail gesendet, mit der Sie Ihr Passwort zurücksetzen können. Überprüfen Sie Ihren Spam-Ordner, wenn Sie die E-Mail nach einigen Minuten noch nicht erhalten haben.", + "successfulResetTitle": "Passwort-Reset erfolgreich", + "successfulReset": "Bitte melden Sie sich auf der Anmeldeseite an.", + "passwordMismatch": "Passwörter stimmen nicht überein", + "invalidLinkTitle": "Ihr Reset-Link ist ungültig", + "invalidLinkHint": "Versuchen Sie erneut, Ihr Passwort zurückzusetzen." + }, + "workflowExecutions": { + "domain": "Arbeitsabläufe", + "subtitle": "Sehen Sie sich Workflow-Ausführungen in Ihrer Medusa-Anwendung an und verfolgen Sie sie.", + "transactionIdLabel": "Transaktions-ID", + "workflowIdLabel": "Workflow-ID", + "progressLabel": "Fortschritt", + "stepsCompletedLabel_one": "{{abgeschlossen}} von {{count}} Schritt", + "stepsCompletedLabel_other": "{{abgeschlossen}} von {{count}} Schritten", + "list": { + "noRecordsMessage": "Es wurden noch keine Workflows ausgeführt." + }, + "history": { + "sectionTitle": "Geschichte", + "runningState": "Läuft...", + "awaitingState": "Warten", + "failedState": "Fehlgeschlagen", + "skippedState": "Übersprungen", + "skippedFailureState": "Übersprungen (Fehler)", + "definitionLabel": "Definition", + "outputLabel": "Ausgabe", + "compensateInputLabel": "Eingabe kompensieren", + "revertedLabel": "Zurückgesetzt", + "errorLabel": "Fehler" + }, + "state": { + "done": "Erledigt", + "failed": "Fehlgeschlagen", + "reverted": "Zurückgesetzt", + "invoking": "Aufrufen", + "compensating": "Kompensierend", + "notStarted": "Nicht gestartet" + }, + "transaction": { + "state": { + "waitingToCompensate": "Ich warte auf eine Entschädigung" + } + }, + "step": { + "state": { + "skipped": "Übersprungen", + "skippedFailure": "Übersprungen (Fehler)", + "dormant": "Ruhend", + "timeout": "Time-out" + } + } + }, + "productTypes": { + "domain": "Produkttypen", + "subtitle": "Organisieren Sie Ihre Produkte in Typen.", + "create": { + "header": "Produkttyp erstellen", + "hint": "Erstellen Sie einen neuen Produkttyp, um Ihre Produkte zu kategorisieren.", + "successToast": "Der Produkttyp {{value}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Produkttyp bearbeiten", + "successToast": "Der Produkttyp {{value}} wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, den Produkttyp {{value}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Der Produkttyp {{value}} wurde erfolgreich gelöscht." + }, + "fields": { + "value": "Wert" + } + }, + "productTags": { + "domain": "Produkt-Tags", + "create": { + "header": "Produkt-Tag erstellen", + "subtitle": "Erstellen Sie ein neues Produkt-Tag, um Ihre Produkte zu kategorisieren.", + "successToast": "Das Produkt-Tag {{value}} wurde erfolgreich erstellt." + }, + "edit": { + "header": "Produkt-Tag bearbeiten", + "subtitle": "Bearbeiten Sie den Wert des Produkt-Tags.", + "successToast": "Das Produkt-Tag {{value}} wurde erfolgreich aktualisiert." + }, + "delete": { + "confirmation": "Sie sind dabei, das Produkt-Tag {{value}} zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.", + "successToast": "Das Produkt-Tag {{value}} wurde erfolgreich gelöscht." + }, + "fields": { + "value": "Wert" + } + }, + "notifications": { + "domain": "Benachrichtigungen", + "emptyState": { + "title": "Keine Benachrichtigungen", + "description": "Sie haben im Moment keine Benachrichtigungen, aber sobald Sie dies tun, werden sie hier angezeigt." + }, + "accessibility": { + "description": "Benachrichtigungen über Medusa-Aktivitäten werden hier aufgelistet." + } + }, + "errors": { + "serverError": "Serverfehler – Versuchen Sie es später noch einmal.", + "invalidCredentials": "Falsche E-Mail oder Passwort" + }, + "statuses": { + "scheduled": "Geplant", + "expired": "Abgelaufen", + "active": "Aktiv", + "enabled": "Ermöglicht", + "disabled": "Deaktiviert" + }, + "labels": { + "productVariant": "Produktvariante", + "prices": "Preise", + "available": "Verfügbar", + "inStock": "Auf Lager", + "added": "Hinzugefügt", + "removed": "ENTFERNT" + }, + "fields": { + "amount": "Menge", + "refundAmount": "Rückerstattungsbetrag", + "name": "Name", + "default": "Standard", + "lastName": "Nachname", + "firstName": "Vorname", + "title": "Titel", + "customTitle": "Benutzerdefinierter Titel", + "manageInventory": "Inventar verwalten", + "inventoryKit": "Mit Inventarset", + "inventoryItems": "Inventargegenstände", + "inventoryItem": "Inventargegenstand", + "requiredQuantity": "Benötigte Menge", + "description": "Beschreibung", + "email": "E-Mail", + "password": "Passwort", + "repeatPassword": "Passwort wiederholen", + "confirmPassword": "Passwort bestätigen", + "newPassword": "Neues Passwort", + "repeatNewPassword": "Neues Passwort wiederholen", + "categories": "Kategorien", + "shippingMethod": "Versandart", + "configurations": "Konfigurationen", + "conditions": "Bedingungen", + "category": "Kategorie", + "collection": "Sammlung", + "discountable": "Rabattierbar", + "handle": "Handhaben", + "subtitle": "Untertitel", + "item": "Artikel", + "qty": "Menge.", + "limit": "Limit", + "tags": "Schlagworte", + "type": "Typ", + "reason": "Grund", + "none": "keiner", + "all": "alle", + "search": "Suchen", + "percentage": "Prozentsatz", + "sales_channels": "Vertriebskanäle", + "customer_groups": "Kundengruppen", + "product_tags": "Produkt-Tags", + "product_types": "Produkttypen", + "product_collections": "Produktkollektionen", + "status": "Status", + "code": "Code", + "value": "Wert", + "disabled": "Deaktiviert", + "dynamic": "Dynamisch", + "normal": "Normal", + "years": "Jahre", + "months": "Monate", + "days": "Tage", + "hours": "Std", + "minutes": "Minuten", + "totalRedemptions": "Gesamteinlösungen", + "countries": "Länder", + "paymentProviders": "Zahlungsanbieter", + "refundReason": "Rückerstattungsgrund", + "fulfillmentProviders": "Fulfillment-Anbieter", + "fulfillmentProvider": "Fulfillment-Anbieter", + "providers": "Anbieter", + "availability": "Verfügbarkeit", + "inventory": "Inventar", + "optional": "Optional", + "note": "Notiz", + "automaticTaxes": "Automatische Steuern", + "taxInclusivePricing": "Preise inklusive Steuern", + "currency": "Währung", + "address": "Adresse", + "address2": "Apartment, Suite usw.", + "city": "Stadt", + "postalCode": "Postleitzahl", + "country": "Land", + "state": "Zustand", + "province": "Provinz", + "company": "Unternehmen", + "phone": "Telefon", + "metadata": "Metadaten", + "selectCountry": "Land auswählen", + "products": "Produkte", + "variants": "Varianten", + "orders": "Bestellungen", + "account": "Konto", + "total": "Bestellsumme", + "paidTotal": "Insgesamt erfasst", + "totalExclTax": "Gesamt exkl. Steuer", + "subtotal": "Zwischensumme", + "shipping": "Versand", + "outboundShipping": "Ausgehender Versand", + "returnShipping": "Rückversand", + "tax": "Steuer", + "created": "Erstellt", + "key": "Schlüssel", + "customer": "Kunde", + "date": "Datum", + "order": "Befehl", + "fulfillment": "Erfüllung", + "provider": "Anbieter", + "payment": "Zahlung", + "items": "Artikel", + "salesChannel": "Vertriebskanal", + "region": "Region", + "discount": "Rabatt", + "role": "Rolle", + "sent": "Gesendet", + "salesChannels": "Vertriebskanäle", + "product": "Produkt", + "createdAt": "Erstellt", + "updatedAt": "Aktualisiert", + "revokedAt": "Widerrufen am", + "true": "WAHR", + "false": "FALSCH", + "giftCard": "Geschenkkarte", + "tag": "Etikett", + "dateIssued": "Ausstellungsdatum", + "issuedDate": "Ausstellungsdatum", + "expiryDate": "Verfallsdatum", + "price": "Preis", + "priceTemplate": "Preis {{regionOrCurrency}}", + "height": "Höhe", + "width": "Breite", + "length": "Länge", + "weight": "Gewicht", + "midCode": "MID-Code", + "hsCode": "HS-Code", + "ean": "EAN", + "upc": "UPC", + "inventoryQuantity": "Bestandsmenge", + "barcode": "Barcode", + "countryOfOrigin": "Ursprungsland", + "material": "Material", + "thumbnail": "Miniaturansicht", + "sku": "Artikelnummer", + "managedInventory": "Verwalteter Bestand", + "allowBackorder": "Rückstand zulassen", + "inStock": "Auf Lager", + "location": "Standort", + "quantity": "Menge", + "variant": "Variante", + "id": "AUSWEIS", + "parent": "Elternteil", + "minSubtotal": "Min. Zwischensumme", + "maxSubtotal": "Max. Zwischensumme", + "shippingProfile": "Versandprofil", + "summary": "Zusammenfassung", + "details": "Einzelheiten", + "label": "Etikett", + "rate": "Rate", + "requiresShipping": "Erfordert Versand", + "unitPrice": "Stückpreis", + "startDate": "Startdatum", + "endDate": "Enddatum", + "draft": "Entwurf", + "values": "Werte" + }, + "dateTime": { + "years_one": "Jahr", + "years_other": "Jahre", + "months_one": "Monat", + "months_other": "Monate", + "weeks_one": "Woche", + "weeks_other": "Wochen", + "days_one": "Tag", + "days_other": "Tage", + "hours_one": "Stunde", + "hours_other": "Stunden", + "minutes_one": "Minute", + "minutes_other": "Minuten", + "seconds_one": "Zweite", + "seconds_other": "Sekunden" + } +} diff --git a/packages/admin/dashboard/src/i18n/translations/index.ts b/packages/admin/dashboard/src/i18n/translations/index.ts index 746565cee9558..21eecb82bcf1a 100644 --- a/packages/admin/dashboard/src/i18n/translations/index.ts +++ b/packages/admin/dashboard/src/i18n/translations/index.ts @@ -1,9 +1,14 @@ +import de from "./de.json" import en from "./en.json" import pl from "./pl.json" + export default { en: { translation: en, }, + de: { + translation: de, + }, pl: { translation: pl, }, diff --git a/packages/core/core-flows/src/cart/workflows/complete-cart.ts b/packages/core/core-flows/src/cart/workflows/complete-cart.ts index e47a5f9973af2..b991a253fc60c 100644 --- a/packages/core/core-flows/src/cart/workflows/complete-cart.ts +++ b/packages/core/core-flows/src/cart/workflows/complete-cart.ts @@ -77,7 +77,7 @@ export const completeCartWorkflow = createWorkflow( const paymentSessions = validateCartPaymentsStep({ cart }) - authorizePaymentSessionStep({ + const payment = authorizePaymentSessionStep({ // We choose the first payment session, as there will only be one active payment session // This might change in the future. id: paymentSessions[0].id, @@ -103,7 +103,17 @@ export const completeCartWorkflow = createWorkflow( } }) - const cartToOrder = transform({ cart }, ({ cart }) => { + const cartToOrder = transform({ cart, payment }, ({ cart, payment }) => { + const transactions = + payment?.captures?.map((capture) => { + return { + amount: capture.raw_amount ?? capture.amount, + currency_code: payment.currency_code, + reference: "capture", + reference_id: capture.id, + } + }) ?? [] + const allItems = (cart.items ?? []).map((item) => { return prepareLineItemData({ item, @@ -158,6 +168,7 @@ export const completeCartWorkflow = createWorkflow( shipping_methods: shippingMethods, metadata: cart.metadata, promo_codes: promoCodes, + transactions, } }) diff --git a/packages/core/core-flows/src/order/steps/add-order-transaction.ts b/packages/core/core-flows/src/order/steps/add-order-transaction.ts index 4e3a2a03d48e3..841f5a0a1e81e 100644 --- a/packages/core/core-flows/src/order/steps/add-order-transaction.ts +++ b/packages/core/core-flows/src/order/steps/add-order-transaction.ts @@ -4,19 +4,44 @@ import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk" export const addOrderTransactionStepId = "add-order-transaction" /** - * This step creates an order transaction. + * This step creates order transactions. */ export const addOrderTransactionStep = createStep( addOrderTransactionStepId, - async (data: CreateOrderTransactionDTO, { container }) => { + async ( + data: CreateOrderTransactionDTO | CreateOrderTransactionDTO[], + { container } + ) => { const service = container.resolve(Modules.ORDER) - const created = await service.addOrderTransactions(data) + const trxsData = Array.isArray(data) ? data : [data] - return new StepResponse(created, created.id) + for (const trx of trxsData) { + const existing = await service.listOrderTransactions( + { + order_id: trx.order_id, + reference: trx.reference, + reference_id: trx.reference_id, + }, + { + select: ["id"], + } + ) + + if (existing.length) { + return new StepResponse(null) + } + } + + const created = await service.addOrderTransactions(trxsData) + + return new StepResponse( + Array.isArray(data) ? created : created[0], + created.map((c) => c.id) + ) }, async (id, { container }) => { - if (!id) { + if (!id?.length) { return } diff --git a/packages/core/core-flows/src/order/workflows/order-edit/order-edit-update-item-quantity.ts b/packages/core/core-flows/src/order/workflows/order-edit/order-edit-update-item-quantity.ts index a70e674f6b65a..176ac2d345a1f 100644 --- a/packages/core/core-flows/src/order/workflows/order-edit/order-edit-update-item-quantity.ts +++ b/packages/core/core-flows/src/order/workflows/order-edit/order-edit-update-item-quantity.ts @@ -4,7 +4,12 @@ import { OrderPreviewDTO, OrderWorkflow, } from "@medusajs/framework/types" -import { ChangeActionType, OrderChangeStatus } from "@medusajs/framework/utils" +import { + BigNumber, + ChangeActionType, + MathBN, + OrderChangeStatus, +} from "@medusajs/framework/utils" import { WorkflowData, WorkflowResponse, @@ -75,19 +80,30 @@ export const orderEditUpdateItemQuantityWorkflow = createWorkflow( const orderChangeActionInput = transform( { order, orderChange, items: input.items }, ({ order, orderChange, items }) => { - return items.map((item) => ({ - order_change_id: orderChange.id, - order_id: order.id, - version: orderChange.version, - action: ChangeActionType.ITEM_UPDATE, - internal_note: item.internal_note, - details: { - reference_id: item.id, - quantity: item.quantity, - unit_price: item.unit_price, - compare_at_unit_price: item.compare_at_unit_price, - }, - })) + return items.map((item) => { + const existing = order?.items?.find( + (exItem) => exItem.id === item.id + )! + + const quantityDiff = new BigNumber( + MathBN.sub(item.quantity, existing.quantity) + ) + + return { + order_change_id: orderChange.id, + order_id: order.id, + version: orderChange.version, + action: ChangeActionType.ITEM_UPDATE, + internal_note: item.internal_note, + details: { + reference_id: item.id, + quantity: item.quantity, + unit_price: item.unit_price, + compare_at_unit_price: item.compare_at_unit_price, + quantity_diff: quantityDiff, + }, + } + }) } ) diff --git a/packages/core/core-flows/src/payment/steps/authorize-payment-session.ts b/packages/core/core-flows/src/payment/steps/authorize-payment-session.ts index 6f6f9ba27cd23..d706e61b7dba8 100644 --- a/packages/core/core-flows/src/payment/steps/authorize-payment-session.ts +++ b/packages/core/core-flows/src/payment/steps/authorize-payment-session.ts @@ -40,7 +40,12 @@ export const authorizePaymentSessionStep = createStep( ) } - const paymentSession = await paymentModule.retrievePaymentSession(input.id) + const paymentSession = await paymentModule.retrievePaymentSession( + input.id, + { + relations: ["payment", "payment.captures"], + } + ) // Throw a special error type when the status is requires_more as it requires a specific further action // from the consumer diff --git a/packages/core/core-flows/src/payment/workflows/capture-payment.ts b/packages/core/core-flows/src/payment/workflows/capture-payment.ts index 49d416e86e37b..a426d562aecf9 100644 --- a/packages/core/core-flows/src/payment/workflows/capture-payment.ts +++ b/packages/core/core-flows/src/payment/workflows/capture-payment.ts @@ -39,13 +39,15 @@ export const capturePaymentWorkflow = createWorkflow( const orderTransactionData = transform( { input, payment, orderPayment }, ({ input, payment, orderPayment }) => { - return { - order_id: orderPayment.order.id, - amount: input.amount ?? payment.raw_amount ?? payment.amount, - currency_code: payment.currency_code, - reference_id: payment.id, - reference: "capture", - } + return payment.captures?.map((capture) => { + return { + order_id: orderPayment.order.id, + amount: input.amount ?? capture.raw_amount ?? capture.amount, + currency_code: payment.currency_code, + reference_id: capture.id, + reference: "capture", + } + }) } ) diff --git a/packages/core/core-flows/src/payment/workflows/index.ts b/packages/core/core-flows/src/payment/workflows/index.ts index ee60681180a57..46de7ec09899e 100644 --- a/packages/core/core-flows/src/payment/workflows/index.ts +++ b/packages/core/core-flows/src/payment/workflows/index.ts @@ -1,5 +1,3 @@ export * from "./capture-payment" -export * from "./on-payment-processed" export * from "./process-payment" export * from "./refund-payment" - diff --git a/packages/core/core-flows/src/payment/workflows/on-payment-processed.ts b/packages/core/core-flows/src/payment/workflows/on-payment-processed.ts deleted file mode 100644 index 9ebc24009389e..0000000000000 --- a/packages/core/core-flows/src/payment/workflows/on-payment-processed.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { WebhookActionResult } from "@medusajs/types" -import { createWorkflow, when } from "@medusajs/workflows-sdk" -import { completeCartWorkflow } from "../../cart" -import { useRemoteQueryStep } from "../../common" -import { useQueryStep } from "../../common/steps/use-query" - -export const onPaymentProcessedWorkflowId = "on-payment-processed-workflow" -export const onPaymentProcessedWorkflow = createWorkflow( - onPaymentProcessedWorkflowId, - (input: WebhookActionResult) => { - const paymentSessionResult = useRemoteQueryStep({ - entry_point: "payment_session", - fields: ["payment_collection_id"], - variables: { filters: { id: input.data?.session_id } }, - list: false, - }) - - const cartPaymentCollection = useQueryStep({ - entity: "cart_payment_collection", - fields: ["cart_id"], - filters: { - payment_collection_id: paymentSessionResult.payment_collection_id, - }, - }) - - when({ cartPaymentCollection }, ({ cartPaymentCollection }) => { - return !!cartPaymentCollection.data.length - }).then(() => { - completeCartWorkflow.runAsStep({ - input: { - id: cartPaymentCollection.data[0].cart_id, - }, - }) - }) - - // TODO: Add more cases down the line, e.g. order payments - } -) diff --git a/packages/core/core-flows/src/payment/workflows/process-payment.ts b/packages/core/core-flows/src/payment/workflows/process-payment.ts index b93516e2fe593..5cfdf5ee82caf 100644 --- a/packages/core/core-flows/src/payment/workflows/process-payment.ts +++ b/packages/core/core-flows/src/payment/workflows/process-payment.ts @@ -1,6 +1,7 @@ import { WebhookActionResult } from "@medusajs/types" import { PaymentActions } from "@medusajs/utils" import { createWorkflow, when } from "@medusajs/workflows-sdk" +import { completeCartWorkflow } from "../../cart/workflows/complete-cart" import { useQueryStep } from "../../common/steps/use-query" import { authorizePaymentSessionStep } from "../steps" import { capturePaymentWorkflow } from "./capture-payment" @@ -15,6 +16,41 @@ export const processPaymentWorkflow = createWorkflow( entity: "payment", fields: ["id"], filters: { payment_session_id: input.data?.session_id }, + }).config({ + name: "payment-query", + }) + + const paymentSessionResult = useQueryStep({ + entity: "payment_session", + fields: ["payment_collection_id"], + filters: { id: input.data?.session_id }, + }).config({ + name: "payment-session-query", + }) + + const cartPaymentCollection = useQueryStep({ + entity: "cart_payment_collection", + fields: ["cart_id"], + filters: { + payment_collection_id: + paymentSessionResult.data[0].payment_collection_id, + }, + }).config({ + name: "cart-payment-query", + }) + + when({ cartPaymentCollection }, ({ cartPaymentCollection }) => { + return !!cartPaymentCollection.data.length + }).then(() => { + completeCartWorkflow + .runAsStep({ + input: { + id: cartPaymentCollection.data[0].cart_id, + }, + }) + .config({ + continueOnPermanentFailure: true, // Continue payment processing even if cart completion fails + }) }) when({ input }, ({ input }) => { @@ -31,8 +67,12 @@ export const processPaymentWorkflow = createWorkflow( }) when({ input }, ({ input }) => { + // Authorize payment session if no Cart is linked to the payment + // When associated with a Cart, the complete cart workflow will handle the authorization return ( - input.action === PaymentActions.AUTHORIZED && !!input.data?.session_id + !cartPaymentCollection.data.length && + input.action === PaymentActions.AUTHORIZED && + !!input.data?.session_id ) }).then(() => { authorizePaymentSessionStep({ diff --git a/packages/core/types/src/payment/common.ts b/packages/core/types/src/payment/common.ts index de4043f63095d..4dca5c8c7e039 100644 --- a/packages/core/types/src/payment/common.ts +++ b/packages/core/types/src/payment/common.ts @@ -471,6 +471,11 @@ export interface CaptureDTO { */ amount: BigNumberValue + /** + * The raw captured amount. + */ + raw_amount?: BigNumberValue + /** * The creation date of the capture. */ @@ -502,6 +507,11 @@ export interface RefundDTO { */ amount: BigNumberValue + /** + * The raw refunded amount. + */ + raw_amount?: BigNumberValue + /** * The id of the refund_reason that is associated with the refund */ diff --git a/packages/medusa/src/commands/db/create.ts b/packages/medusa/src/commands/db/create.ts index d8aac928e806b..5dd2d6b48ad85 100644 --- a/packages/medusa/src/commands/db/create.ts +++ b/packages/medusa/src/commands/db/create.ts @@ -138,7 +138,7 @@ export async function dbCreate({ if (await dbExists(client, dbName)) { logger.info(`Database "${dbName}" already exists`) - envEditor.set("DB_NAME", dbName) + envEditor.set("DB_NAME", dbName, { withEmptyTemplateValue: true }) await envEditor.save() logger.info(`Updated .env file with "DB_NAME=${dbName}"`) diff --git a/packages/medusa/src/subscribers/payment-webhook.ts b/packages/medusa/src/subscribers/payment-webhook.ts index b8c56f8f6d9eb..895159adc3e3a 100644 --- a/packages/medusa/src/subscribers/payment-webhook.ts +++ b/packages/medusa/src/subscribers/payment-webhook.ts @@ -1,7 +1,4 @@ -import { - onPaymentProcessedWorkflow, - processPaymentWorkflow, -} from "@medusajs/core-flows" +import { processPaymentWorkflow } from "@medusajs/core-flows" import { IPaymentModuleService, ProviderWebhookPayload, @@ -29,7 +26,7 @@ export default async function paymentWebhookhandler({ const input = event.data if ( - (input.payload.rawData as unknown as SerializedBuffer).type === "Buffer" + (input.payload?.rawData as unknown as SerializedBuffer)?.type === "Buffer" ) { input.payload.rawData = Buffer.from( (input.payload.rawData as unknown as SerializedBuffer).data @@ -49,11 +46,6 @@ export default async function paymentWebhookhandler({ await processPaymentWorkflow(container).run({ input: processedEvent, }) - - // We process the intended side effects of payment processing separately. - await onPaymentProcessedWorkflow(container).run({ - input: processedEvent, - }) } export const config: SubscriberConfig = { diff --git a/packages/modules/event-bus-local/src/services/__tests__/event-bus-local.ts b/packages/modules/event-bus-local/src/services/__tests__/event-bus-local.ts index c31da43b8ffa4..89b651f493b42 100644 --- a/packages/modules/event-bus-local/src/services/__tests__/event-bus-local.ts +++ b/packages/modules/event-bus-local/src/services/__tests__/event-bus-local.ts @@ -123,6 +123,7 @@ describe("LocalEventBusService", () => { data: { test: "1234" }, metadata: { eventGroupId: "test" }, name: "test-event", + options: {}, }) jest.clearAllMocks() diff --git a/packages/modules/event-bus-local/src/services/event-bus-local.ts b/packages/modules/event-bus-local/src/services/event-bus-local.ts index 5ad982a54a0b7..3d33cf5cc13c8 100644 --- a/packages/modules/event-bus-local/src/services/event-bus-local.ts +++ b/packages/modules/event-bus-local/src/services/event-bus-local.ts @@ -9,6 +9,7 @@ import { } from "@medusajs/framework/types" import { AbstractEventBusModuleService } from "@medusajs/framework/utils" import { EventEmitter } from "events" +import { setTimeout } from "timers/promises" import { ulid } from "ulid" type InjectedDependencies = { @@ -69,7 +70,10 @@ export default class LocalEventBusService extends AbstractEventBusModuleService ) } - await this.groupOrEmitEvent(eventData) + await this.groupOrEmitEvent({ + ...eventData, + options, + }) } } @@ -86,7 +90,13 @@ export default class LocalEventBusService extends AbstractEventBusModuleService await this.groupEvent(eventGroupId, eventData) } else { const { options, ...eventBody } = eventData - this.eventEmitter_.emit(eventData.name, eventBody) + + const options_ = options as { delay: number } + const delay = options?.delay ? setTimeout : async () => {} + + delay(options_?.delay).then(() => + this.eventEmitter_.emit(eventData.name, eventBody) + ) } } @@ -108,7 +118,12 @@ export default class LocalEventBusService extends AbstractEventBusModuleService for (const event of groupedEvents) { const { options, ...eventBody } = event - this.eventEmitter_.emit(event.name, eventBody) + const options_ = options as { delay: number } + const delay = options?.delay ? setTimeout : async () => {} + + delay(options_?.delay).then(() => + this.eventEmitter_.emit(event.name, eventBody) + ) } await this.clearGroupedEvents(eventGroupId) diff --git a/www/apps/book/.gitignore b/www/apps/book/.gitignore index 25bbb4b9c1180..98297a172f7b0 100644 --- a/www/apps/book/.gitignore +++ b/www/apps/book/.gitignore @@ -15,6 +15,7 @@ # production /build +!/app/learn/build # misc .DS_Store diff --git a/www/apps/book/app/_not-found.mdx b/www/apps/book/app/_not-found.mdx index 914b2b8541129..fa69e0b22d4d4 100644 --- a/www/apps/book/app/_not-found.mdx +++ b/www/apps/book/app/_not-found.mdx @@ -23,7 +23,7 @@ If you think this is a mistake, please [report this issue on GitHub](https://git items={[ { title: "Get Started Docs", - href: "/", + href: "/learn", icon: BookOpen }, { diff --git a/www/apps/book/app/learn/advanced-development/admin/tips/page.mdx b/www/apps/book/app/learn/advanced-development/admin/tips/page.mdx index 1975cc38aa1ca..e1bf3f817cef4 100644 --- a/www/apps/book/app/learn/advanced-development/admin/tips/page.mdx +++ b/www/apps/book/app/learn/advanced-development/admin/tips/page.mdx @@ -55,7 +55,7 @@ import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types" const ProductWidget = () => { const { data, isLoading } = useQuery({ queryFn: () => sdk.admin.product.list(), - queryKey: ["products"] + queryKey: ["products"], }) return ( @@ -95,17 +95,17 @@ import { sdk } from "../lib/config" import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types" const ProductWidget = ({ - data: productData + data: productData, }: DetailWidgetProps) => { const { mutateAsync } = useMutation({ mutationFn: (payload: HttpTypes.AdminUpdateProduct) => sdk.admin.product.update(productData.id, payload), - onSuccess: () => alert("updated product") + onSuccess: () => alert("updated product"), }) const handleUpdate = () => { mutateAsync({ - title: "New Product Title" + title: "New Product Title", }) } diff --git a/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx b/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx index 4523156ab60b2..5587d6f00a6b9 100644 --- a/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx +++ b/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx @@ -95,6 +95,12 @@ export const GET = async ( The value of `req.query.name` is the value passed in `?name=John`, for example. +### Validate Query Parameters + +You can apply validation rules on received query parameters to ensure they match specified rules and types. + +Learn more in [this documentation](../validation/page.mdx#how-to-validate-request-query-paramters). + --- ## Request Body Parameters @@ -153,3 +159,9 @@ This returns the following JSON object: "message": "[POST] Hello John!" } ``` + +### Validate Body Parameters + +You can apply validation rules on received body parameters to ensure they match specified rules and types. + +Learn more in [this documentation](../validation/page.mdx#how-to-validate-request-body). diff --git a/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx b/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx index 8c5669ddd756a..ad6177ef33404 100644 --- a/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx +++ b/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx @@ -1,24 +1,33 @@ export const metadata = { - title: `${pageNumber} Request Body Parameter Validation`, + title: `${pageNumber} Request Body and Query Parameter Validation`, } # {metadata.title} -In this chapter, you'll learn how to validate request body parameters in your custom API route. +In this chapter, you'll learn how to validate request body and query parameters in your custom API route. -## Example Scenario +## Request Validation Consider you're creating a `POST` API route at `/custom`. It accepts two parameters `a` and `b` that are required numbers, and returns their sum. -The next steps explain how to add validation to this API route, as an example. +Medusa provides two middlewares to validate the request body and query paramters of incoming requests to your custom API routes: + +- `validateAndTransformBody` to validate the request's body parameters against a schema. +- `validateAndTransformQuery` to validate the request's query parameters against a schema. + +Both middlewares accept a [Zod](https://zod.dev/) schema as a parameter, which gives you flexibility in how you define your validation schema with complex rules. + +The next steps explain how to add request body and query parameter validation to the API route mentioned earlier. --- -## Step 1: Create Zod Schema +## How to Validate Request Body -Medusa uses [Zod](https://zod.dev/) to validate the body parameters of an incoming request. +### Step 1: Create Validation Schema -To use Zod to validate your custom schemas, create a `validators.ts` file in any `src/api` subfolder. This file holds Zod schemas for each of your API routes. +Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters. + +To create a validation schema with Zod, create a `validators.ts` file in any `src/api` subfolder. This file holds Zod schemas for each of your API routes. For example, create the file `src/api/custom/validators.ts` with the following content: @@ -37,11 +46,9 @@ The `PostStoreCustomSchema` variable is a Zod schema that indicates the request 2. It has a property `a` that is a required number. 3. It has a property `b` that is a required number. ---- - -## Step 2: Add Validation Middleware +### Step 2: Add Request Body Validation Middleware -To use this schema for validating the body parameters of requests to `/custom`, use the `validateAndTransformBody` middleware provided by `@medusajs/framework/utils`. It accepts the Zod schema as a parameter. +To use this schema for validating the body parameters of requests to `/custom`, use the `validateAndTransformBody` middleware provided by `@medusajs/framework/http`. It accepts the Zod schema as a parameter. For example, create the file `src/api/middlewares.ts` with the following content: @@ -49,7 +56,7 @@ For example, create the file `src/api/middlewares.ts` with the following content import { defineMiddlewares } from "@medusajs/medusa" import { validateAndTransformBody, -} from "@medusajs/framework/utils" +} from "@medusajs/framework/http" import { PostStoreCustomSchema } from "./custom/validators" export default defineMiddlewares({ @@ -67,15 +74,13 @@ export default defineMiddlewares({ This applies the `validateAndTransformBody` middleware on `POST` requests to `/custom`. It uses the `PostStoreCustomSchema` as the validation schema. -### How the Validation Works +#### How the Validation Works If a request's body parameters don't pass the validation, the `validateAndTransformBody` middleware throws an error indicating the validation errors. If a request's body parameters are validated successfully, the middleware sets the validated body parameters in the `validatedBody` property of `MedusaRequest`. ---- - -## Step 3: Use Validated Body in API Route +### Step 3: Use Validated Body in API Route In your API route, consume the validated body using the `validatedBody` property of `MedusaRequest`. @@ -113,11 +118,131 @@ To pass the request body's type as a type parameter to `MedusaRequest`, use Zod' +### Test it Out + +To test out the validation, send a `POST` request to `/custom` passing `a` and `b` body parameters. You can try sending incorrect request body parameters to test out the validation. + +For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data: + +```json +{ + "type": "invalid_data", + "message": "Invalid request: Field 'a' is required" +} +``` + --- -## Test it Out +## How to Validate Request Query Paramters + +The steps to validate the request query parameters are the similar to that of [validating the body](#how-to-validate-request-body). + +### Step 1: Create Validation Schema + +The first step is to create a schema with Zod with the rules of the accepted query parameters. + +Consider that the API route accepts two query parameters `a` and `b` that are numbers, similar to the previous section. + +Create the file `src/api/custom/validators.ts` with the following content: + +```ts title="src/api/custom/validators.ts" +import { z } from "zod" + +export const PostStoreCustomSchema = z.object({ + a: z.preprocess( + (val) => { + if (val && typeof val === "string") { + return parseInt(val) + } + return val + }, + z + .number() + ), + b: z.preprocess( + (val) => { + if (val && typeof val === "string") { + return parseInt(val) + } + return val + }, + z + .number() + ), +}) +``` + +Since a query parameter's type is originally a string or array of strings, you have to use Zod's `preprocess` method to validate other query types, such as numbers. + +For both `a` and `b`, you transform the query parameter's value to an integer first if it's a string, then, you check that the resulting value is a number. + +### Step 2: Add Request Query Validation Middleware + +Next, you'll use the schema to validate incoming requests' query parameters to the `/custom` API route. + +Add the `validateAndTransformQuery` middleware to the API route in the file `src/api/middlewares.ts`: + +```ts title="src/api/middlewares.ts" +import { defineMiddlewares } from "@medusajs/medusa" +import { + validateAndTransformQuery, +} from "@medusajs/framework/http" +import { PostStoreCustomSchema } from "./custom/validators" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/custom", + method: "POST", + middlewares: [ + validateAndTransformQuery( + PostStoreCustomSchema, + {} + ), + ], + }, + ], +}) +``` + +The `validateAndTransformQuery` accepts two parameters: + +- The first one is the Zod schema to validate the query parameters against. +- The second one is an object of options for retrieving data using Query, which you can learn more about in [this chapter](../../module-links/query/page.mdx). + +#### How the Validation Works + +If a request's query parameters don't pass the validation, the `validateAndTransformQuery` middleware throws an error indicating the validation errors. + +If a request's query parameters are validated successfully, the middleware sets the validated query parameters in the `validatedQuery` property of `MedusaRequest`. + +### Step 3: Use Validated Query in API Route + +Finally, use the validated query in the API route. The `MedusaRequest` parameter has a `validatedQuery` parameter that you can use to access the validated parameters. + +For example, create the file `src/api/custom/route.ts` with the following content: + +```ts title="src/api/custom/route.ts" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const a = req.validatedQuery.a as number + const b = req.validatedQuery.b as number + + res.json({ + sum: a + b + }) +} +``` + +In the API route, you use the `validatedQuery` property of `MedusaRequest` to access the values of the `a` and `b` properties as numbers, then return in the response their sum. + +### Test it Out -To test out the validation, send a `POST` request to `/custom`. You can try sending incorrect request body parameters. +To test out the validation, send a `POST` request to `/custom` with `a` and `b` query parameters. You can try sending incorrect query parameters to see how the validation works. For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data: diff --git a/www/apps/book/app/learn/advanced-development/events-and-subscribers/emit-event/page.mdx b/www/apps/book/app/learn/advanced-development/events-and-subscribers/emit-event/page.mdx index 7f768aa385a34..70635f5b12157 100644 --- a/www/apps/book/app/learn/advanced-development/events-and-subscribers/emit-event/page.mdx +++ b/www/apps/book/app/learn/advanced-development/events-and-subscribers/emit-event/page.mdx @@ -1,22 +1,50 @@ +import { CodeTabs, CodeTab } from "docs-ui" + export const metadata = { - title: `${pageNumber} Emit an Event`, + title: `${pageNumber} Emit Workflow and Service Events`, } # {metadata.title} -In this chapter, you'll learn how to emit an event in a workflow. +In this chapter, you'll learn about event types and how to emit an event in a service or workflow. + +## Event Types + +In your customization, you can emit an event, then listen to it in a subscriber and perform an asynchronus action, such as send a notification or data to a third-party system. + +There are two types of events in Medusa: + +1. Worflow event: an event that's emitted in a workflow after a commerce feature is performed. For example, Medusa emits the `order.placed` event after a cart is completed. +2. Service event: an event that's emitted to track, trace, or debug processes under the hood. For example, you can emit an event with an audit trail. + +### Which Event Type to Use? + +**Workflow events** are the most common event type in development, as most custom features and customizations are built around workflows. + +Some examples of workflow events: -## Emit Event Step +1. When a user creates a blog post and you're emitting an event to send a newsletter email. +2. When you finish syncing products to a third-party system and you want to notify the admin user of new products added. +3. When a customer purchases a digital product and you want to generate and send it to them. -Medusa provides an `emitEventStep` helper step in the `@medusajs/medusa/core-flows` package that emits an event. +You should only go for a **service event** if you're emitting an event for processes under the hood that don't directly affect front-facing features. -When you emit an event, you specify the event's name and data payload to pass with the event. +Some examples of service events: + +1. When you're tracing data manipulation and changes, and you want to track every time some custom data is changed. +2. When you're syncing data with a search engine. + +--- + +## Emit Event in a Workflow + +To emit a workflow event, use the `emitEventStep` helper step provided in the `@medusajs/medusa/core-flows` package. For example: export const highlights = [ ["13", "emitEventStep", "Emit an event."], - ["14", `"custom.created"`, "The name of the event to emit."], + ["14", `eventName`, "The name of the event to emit."], ["15", "data", "The data payload to pass with the event."] ] @@ -37,12 +65,129 @@ const helloWorldWorkflow = createWorkflow( eventName: "custom.created", data: { id: "123", + // other data payload }, }) } ) ``` +The `emitEventStep` accepts an object having the following properties: + +- `eventName`: The event's name. +- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. + In this example, you emit the event `custom.created` and pass in the data payload an ID property. -If you execute the workflow, the emit is emitted and any subscribers listening to the event are executed. +### Test it Out + +If you execute the workflow, the event is emitted and you can see it in your application's logs. + +Any subscribers listening to the event are executed. + +--- + +## Emit Event in a Service + +To emit a service event: + +1. Resolve `event_bus` from the module's container in your service's constructor: + + + + +```ts title="src/modules/hello/service.ts" highlights={["9"]} +import { IEventBusService } from "@medusajs/framework/types" +import { MedusaService } from "@medusajs/framework/utils" + +class HelloModuleService extends MedusaService({ + MyCustom, +}){ + protected eventBusService_: AbstractEventBusModuleService + + constructor({ event_bus }) { + super(...arguments) + this.eventBusService_ = event_bus + } +} +``` + + + + +```ts title="src/modules/hello/service.ts" highlights={["6"]} +import { IEventBusService } from "@medusajs/framework/types" + +class HelloModuleService { + protected eventBusService_: AbstractEventBusModuleService + + constructor({ event_bus }) { + this.eventBusService_ = event_bus + } +} +``` + + + + +2. Use the event bus service's `emit` method in the service's methods to emit an event: + +export const serviceHighlights = [ + ["6", "emit", "Emit an event."], + ["7", "name", "The name of the event to emit."], + ["8", "data", "The data payload to pass with the event."] +] + +```ts title="src/modules/hello/service.ts" highlights={serviceHighlights} +class HelloModuleService { + // ... + performAction() { + // TODO perform action + + this.eventBusService_.emit({ + name: "custom.event", + data: { + id: "123", + // other data payload + } + }) + } +} +``` + +The method accepts an object having the following properties: + +- `name`: The event's name. +- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. + +3. By default, the Event Module's service isn't injected into your module's container. To add it to the container, pass it in the module's registration object in `medusa-config.ts` in the `dependencies` property: + +export const depsHighlight = [ + ["8", "dependencies", "An array of module registration names to inject into the Module's container."], +] + +```ts title="medusa-config.ts" highlights={depsHighlight} +import { Modules } from '@medusajs/framework/utils' + +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "./src/modules/hello", + dependencies: [ + Modules.EVENT_BUS + ] + } + ] +}) +``` + +The `dependencies` property accepts an array of module registration keys. The specified modules' main services are injected into the module's container. + +That's how you can resolve it in your module's main service's constructor. + +### Test it Out + +If you execute the `performAction` method of your service, the event is emitted and you can see it in your application's logs. + +Any subscribers listening to the event are also executed. diff --git a/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx b/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx index cb3a50b3198e4..d1b0b542bac03 100644 --- a/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx +++ b/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx @@ -8,12 +8,6 @@ export const metadata = { In this chapter, you’ll learn about the Query utility and how to use it to fetch data from modules. - - -Query is in development and is subject to change in future releases. - - - ## What is Query? Query fetches data across modules. It’s a set of methods registered in the Medusa container under the `query` key. @@ -227,3 +221,126 @@ When you provide the pagination fields, the `query.graph` method's returned obje description: "The total number of records." } ]} sectionTitle="Apply Pagination" /> + +--- + +## Request Query Configurations + +For API routes that retrieve a single or list of resources, Medusa provides a `validateAndTransformQuery` middleware that: + +- Validates accepted query parameters, as explained in [this documentation](../../api-routes/validation/page.mdx). +- Parses configurations that are received as query parameters to be passed to Query. + +Using this middleware allows you to have default configurations for retrieved fields and relations or pagination, while allowing clients to customize them per request. + +### Step 1: Add Middleware + +The first step is to use the `validateAndTransformQuery` middleware on the `GET` route. You add the middleware in `src/api/middlewares.ts`: + +```ts title="src/api/middlewares.ts" +import { defineMiddlewares } from "@medusajs/medusa" +import { + validateAndTransformQuery, +} from "@medusajs/framework/http" +import { createFindParams } from "@medusajs/medusa/api/utils/validators" + +export const GetCustomSchema = createFindParams() + +export default defineMiddlewares({ + routes: [ + { + matcher: "/customs", + method: "GET", + middlewares: [ + validateAndTransformQuery( + GetCustomSchema, + { + defaults: [ + "id", + "name", + "products.*" + ], + isList: true + } + ), + ], + }, + ], +}) +``` + +The `validateAndTransformQuery` accepts two parameters: + +1. A Zod validation schema for the query parameters, which you can learn more about in the [API Route Validation documentation](../../api-routes/validation/page.mdx). Medusa has a `createFindParams` utility that generates a Zod schema that accepts four query parameters: + 1. `fields`: The fields and relations to retrieve in the returned resources. + 2. `offset`: The number of items to skip before retrieving the returned items. + 3. `limit`: The maximum number of items to return. + 4. `order`: The fields to order the returned items by in ascending or descending order. +2. A Query configuration object. It accepts the following properties: + 1. `defaults`: An array of default fields and relations to retrieve in each resource. + 2. `isList`: A boolean indicating whether a list of items are returned in the response. + 3. `allowed`: An array of fields and relations allowed to be passed in the `fields` query parameter. + 4. `defaultLimit`: A number indicating the default limit to use if no limit is provided. By default, it's `50`. + +### Step 2: Use Configurations in API Route + +After applying this middleware, your API route now accepts the `fields`, `offset`, `limit`, and `order` query parameters mentioned above. + +The middleware transforms these parameters to configurations that you can pass to Query in your API route handler. These configurations are stored in the `remoteQueryConfig` parameter of the `MedusaRequest` object. + +For example, Create the file `src/api/customs/route.ts` with the following content: + +export const queryConfigHighlights = [ + ["17", "req.remoteQueryConfig", "Pass the parsed request Query configurations to the Query graph execution."] +] + +```ts title="src/api/customs/route.ts" +import { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" + +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) + + const { data: myCustoms } = await query.graph({ + entity: "my_custom", + ...req.remoteQueryConfig + }) + + res.json({ my_customs: myCustoms }) +} +``` + +This adds a `GET` API route at `/customs`, which is the API route you added the middleware for. + +In the API route, you pass `req.remoteQueryConfig` to `query.graph`. `remoteQueryConfig` has properties like `fields` and `pagination` to configure the query based on the default values you specified in the middleware, and the query parameters passed in the request. + +### Test it Out + +To test it out, start your Medusa application and send a `GET` request to the `/customs` API route. A list of records are retrieved with the specified fields in the middleware. + +```json title="Returned Data" +{ + "my_customs": [ + { + "id": "123", + "name": "test" + } + ] +} +``` + +Try passing one of the Query configuration parameters, like `fields` or `limit`, and you'll see its impact on the returned result. + + + +Learn more about [specifing fields and relations](!api!/store#select-fields-and-relations) and [pagination](!api!/store#pagination) in the API reference. + + diff --git a/www/apps/book/app/learn/advanced-development/modules/container/page.mdx b/www/apps/book/app/learn/advanced-development/modules/container/page.mdx index 18cd48805bcab..b0d6cd6628b4c 100644 --- a/www/apps/book/app/learn/advanced-development/modules/container/page.mdx +++ b/www/apps/book/app/learn/advanced-development/modules/container/page.mdx @@ -57,7 +57,7 @@ import { LoaderOptions, } from "@medusajs/framework/types" import { - ContainerRegistrationKeys + ContainerRegistrationKeys, } from "@medusajs/framework/utils" export default async function helloWorldLoader({ diff --git a/www/apps/book/app/learn/advanced-development/modules/options/page.mdx b/www/apps/book/app/learn/advanced-development/modules/options/page.mdx index d6bd8cae47efb..7635e9095b336 100644 --- a/www/apps/book/app/learn/advanced-development/modules/options/page.mdx +++ b/www/apps/book/app/learn/advanced-development/modules/options/page.mdx @@ -30,7 +30,7 @@ module.exports = defineConfig({ capitalize: true, }, }, - ] + ], }) ``` diff --git a/www/apps/book/app/learn/advanced-development/modules/service-constraints/page.mdx b/www/apps/book/app/learn/advanced-development/modules/service-constraints/page.mdx index 30e6ad83eeb48..6420aa7098e0b 100644 --- a/www/apps/book/app/learn/advanced-development/modules/service-constraints/page.mdx +++ b/www/apps/book/app/learn/advanced-development/modules/service-constraints/page.mdx @@ -10,7 +10,7 @@ This chapter lists constraints to keep in mind when creating a service. Medusa wraps service method executions to inject useful context or transactions. However, since Medusa can't detect whether the method is asynchronous, it always executes methods in the wrapper with the `await` keyword. -For example, if you have a synchronous `getMessage` method, and you use it other resources like workflows, Medusa executes it as an async method: +For example, if you have a synchronous `getMessage` method, and you use it in other resources like workflows, Medusa executes it as an async method: ```ts await helloModuleService.getMessage() diff --git a/www/apps/book/app/learn/advanced-development/workflows/constructor-constraints/page.mdx b/www/apps/book/app/learn/advanced-development/workflows/constructor-constraints/page.mdx index d1136dceaa4b1..6223860ebacb9 100644 --- a/www/apps/book/app/learn/advanced-development/workflows/constructor-constraints/page.mdx +++ b/www/apps/book/app/learn/advanced-development/workflows/constructor-constraints/page.mdx @@ -145,7 +145,7 @@ const myWorkflow = createWorkflow( function (input: WorkflowInput) { const message = transform( { - input + input, }, (data) => data.input.message || "hello" ) diff --git a/www/apps/book/app/learn/advanced-development/workflows/multiple-step-usage/page.mdx b/www/apps/book/app/learn/advanced-development/workflows/multiple-step-usage/page.mdx new file mode 100644 index 0000000000000..91db3d58c3de5 --- /dev/null +++ b/www/apps/book/app/learn/advanced-development/workflows/multiple-step-usage/page.mdx @@ -0,0 +1,80 @@ +export const metadata = { + title: `${pageNumber} Multiple Step Usage in Workflow`, +} + +# {metadata.title} + +In this chapter, you'll learn how to use a step multiple times in a workflow. + +## Problem Reusing a Step in a Workflow + +In some cases, you may need to use a step multiple times in the same workflow. + +The most common example is using the `useQueryGraphStep` multiple times in a workflow to retrieve multiple unrelated data, such as customers and products. + +Each workflow step must have a unique ID, which is the ID passed as a first parameter when creating the step: + +```ts +const useQueryGraphStep = createStep( + "use-query-graph", + // ... +) +``` + +This causes an error when you use the same step multiple times in a workflow, as it's registered in the workflow as two steps having the same ID: + +```ts +const helloWorkflow = createWorkflow( + "hello", + () => { + const { data: products } = useQueryGraphStep({ + entity: "product", + fields: ["id"] + }) + + // ERROR OCCURS HERE: A STEP HAS THE SAME ID AS ANOTHER IN THE WORKFLOW + const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: ["id"] + }) + } +) +``` + +The next section explains how to fix this issue to use the same step multiple times in a workflow. + +--- + +## How to Use a Step Multiple Times in a Workflow? + +When you execute a step in a workflow, you can chain a `config` method to it to change the step's config. + +Use the `config` method to change a step's ID for a single execution. + +So, this is the correct way to write the example above: + +export const highlights = [ + ["13", "name", "Change the step's ID for this execution."] +] + +```ts highlights={highlights} +const helloWorkflow = createWorkflow( + "hello", + () => { + const { data: products } = useQueryGraphStep({ + entity: "product", + fields: ["id"] + }) + + // ✓ No error occurs, the step has a different ID. + const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: ["id"] + }).config({ name: "fetch-customers" }) + } +) +``` + +The `config` method accepts an object with a `name` property. Its value is a new ID of the step to use for this execution only. + +The first `useQueryGraphStep` usage has the ID `use-query-graph`, and the second `useQueryGraphStep` usage has the ID `fetch-customers`. diff --git a/www/apps/book/app/learn/advanced-development/workflows/variable-manipulation/page.mdx b/www/apps/book/app/learn/advanced-development/workflows/variable-manipulation/page.mdx index e3f84a2a5c780..42a41ce84cafc 100644 --- a/www/apps/book/app/learn/advanced-development/workflows/variable-manipulation/page.mdx +++ b/www/apps/book/app/learn/advanced-development/workflows/variable-manipulation/page.mdx @@ -179,7 +179,7 @@ const myWorkflow = createWorkflow( "hello-world", function (input) { validateHasStr1({ - input + input, }) // workflow continues its execution only if diff --git a/www/apps/book/app/learn/basics/modules/page.mdx b/www/apps/book/app/learn/basics/modules/page.mdx index ccefdce4436be..aca04b00bbad5 100644 --- a/www/apps/book/app/learn/basics/modules/page.mdx +++ b/www/apps/book/app/learn/basics/modules/page.mdx @@ -135,8 +135,8 @@ module.exports = defineConfig({ modules: [ { resolve: "./src/modules/hello", - } - ] + }, + ], }) ``` @@ -212,11 +212,11 @@ export async function GET( ) const my_custom = await helloModuleService.createMyCustoms({ - name: "test" + name: "test", }) res.json({ - my_custom + my_custom, }) } ``` diff --git a/www/apps/book/app/learn/build/page.mdx b/www/apps/book/app/learn/build/page.mdx new file mode 100644 index 0000000000000..d0cc43bee76e6 --- /dev/null +++ b/www/apps/book/app/learn/build/page.mdx @@ -0,0 +1,80 @@ +export const metadata = { + title: `${pageNumber} Build Medusa Application`, +} + +# {metadata.title} + +In this chapter, you'll learn how to create a production build of your Medusa application to be deployed to a hosting provider. + +Next chapters explain how to deploy the Medusa application. + +## build Command + +The Medusa CLI tool has a [build](!resources!/medusa-cli/commands/build) command which creates a standalone build of the Medusa application that: + +- Doesn't rely on the source TypeScript files. +- Can be copied to a production server reliably. + +So, to create the production build, run the following command in the root of your Medusa application: + +```bash +npx medusa build +``` + +--- + +## Build Output + +The `build` command outputs the production build in the `.medusa/server` directory, and the admin dashboard build in the `.medusa/server/public/admin`. + +### Separate Admin Build + +The `build` command accepts a `--admin-only` option that outputs the admin to the `.medusa/admin` directory. This is useful when deploying the admin dashboard separately, such as on Vercel: + +```bash +npx medusa build --admin-only +``` + +--- + +## Start Built Medusa Application + +To start the Medusa application after running the `build` command: + +- Change to the `.medusa/server` directory and install the dependencies: + +```bash npm2yarn +cd .medusa/server && npm install +``` + +- When running the application locally, make sure to copy the `.env` file from the root project's directory. In production, use system environment variables instead. + +```bash title=".medusa/server" +cp ../../.env .env.production +``` + + + +When `NODE_ENV=production`, the Medusa application loads the environment variables from `.env.production`. Learn more about environment variables in [this guide](../advanced-development/environment-variables/page.mdx). + + + +- In the system environment variables, set `NODE_ENV` to `production`: + +```bash +NODE_ENV=production +``` + +- Use the `start` command in the `.medusa/server` directory to run the application: + +```bash npm2yarn title=".medusa/server" +npm run start +``` + +--- + +## Deploying Production Build + +The next chapter covers how you generally deploy the production build. + +You can also refer to the [deployment how-to guides](!resources!/deployment) for platform-specific how-to guides. diff --git a/www/apps/book/app/learn/customization/custom-features/api-route/page.mdx b/www/apps/book/app/learn/customization/custom-features/api-route/page.mdx index 34c1d5869ec7d..462629566288d 100644 --- a/www/apps/book/app/learn/customization/custom-features/api-route/page.mdx +++ b/www/apps/book/app/learn/customization/custom-features/api-route/page.mdx @@ -16,7 +16,7 @@ This chapter covers how to define an API route that creates a brand as the last items={[ { text: "createBrandWorkflow", - link: "/customization/custom-features/workflow" + link: "/learn/customization/custom-features/workflow" } ]} /> diff --git a/www/apps/book/app/learn/customization/custom-features/module/page.mdx b/www/apps/book/app/learn/customization/custom-features/module/page.mdx index 9dc8ac4f2d3f4..eacdc739fd4d0 100644 --- a/www/apps/book/app/learn/customization/custom-features/module/page.mdx +++ b/www/apps/book/app/learn/customization/custom-features/module/page.mdx @@ -105,8 +105,8 @@ module.exports = defineConfig({ modules: [ { resolve: "./src/modules/brand", - } - ] + }, + ], }) ``` diff --git a/www/apps/book/app/learn/customization/custom-features/workflow/page.mdx b/www/apps/book/app/learn/customization/custom-features/workflow/page.mdx index cd283ecdfdb25..ab66d555f30e4 100644 --- a/www/apps/book/app/learn/customization/custom-features/workflow/page.mdx +++ b/www/apps/book/app/learn/customization/custom-features/workflow/page.mdx @@ -34,7 +34,7 @@ This is even more useful when you create workflows with many steps, or integrate items={[ { text: "Brand Module", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" } ]} /> diff --git a/www/apps/book/app/learn/customization/customize-admin/route/page.mdx b/www/apps/book/app/learn/customization/customize-admin/route/page.mdx index 01ddb07221212..049cb0b6edb96 100644 --- a/www/apps/book/app/learn/customization/customize-admin/route/page.mdx +++ b/www/apps/book/app/learn/customization/customize-admin/route/page.mdx @@ -26,7 +26,7 @@ The UI Route can be shown in the sidebar or added as a nested page. items={[ { text: "Brand Module", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" }, ]} /> diff --git a/www/apps/book/app/learn/customization/customize-admin/widget/page.mdx b/www/apps/book/app/learn/customization/customize-admin/widget/page.mdx index e4b4cea99d4b2..df354ac619488 100644 --- a/www/apps/book/app/learn/customization/customize-admin/widget/page.mdx +++ b/www/apps/book/app/learn/customization/customize-admin/widget/page.mdx @@ -18,7 +18,7 @@ This chapter covers how to show the brand of a product in the Medusa Admin using items={[ { text: "Retrieve Brand of Product API Route", - link: "/customization/extend-models/query-linked-records" + link: "/learn/customization/extend-models/query-linked-records" } ]} /> diff --git a/www/apps/book/app/learn/customization/extend-models/define-link/page.mdx b/www/apps/book/app/learn/customization/extend-models/define-link/page.mdx index 0891dbfc979df..ebc1c22600a28 100644 --- a/www/apps/book/app/learn/customization/extend-models/define-link/page.mdx +++ b/www/apps/book/app/learn/customization/extend-models/define-link/page.mdx @@ -18,7 +18,7 @@ This chapter covers how to define a link between the `Brand` and `Product`data m items={[ { text: "Brand Module having a Brand data model", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" } ]} /> diff --git a/www/apps/book/app/learn/customization/extend-models/extend-create-product/page.mdx b/www/apps/book/app/learn/customization/extend-models/extend-create-product/page.mdx index 962e8fdbc243c..17c0f3e165ef9 100644 --- a/www/apps/book/app/learn/customization/extend-models/extend-create-product/page.mdx +++ b/www/apps/book/app/learn/customization/extend-models/extend-create-product/page.mdx @@ -26,11 +26,11 @@ It's useful when you want to pass custom data, such as the brand ID, then perfor items={[ { text: "Brand Module", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" }, { text: "Defined link between the Brand and Product data models.", - link: "/customization/extend-models/define-link" + link: "/learn/customization/extend-models/define-link" } ]} /> diff --git a/www/apps/book/app/learn/customization/extend-models/query-linked-records/page.mdx b/www/apps/book/app/learn/customization/extend-models/query-linked-records/page.mdx index 6a56da474f318..3a4ba06e02440 100644 --- a/www/apps/book/app/learn/customization/extend-models/query-linked-records/page.mdx +++ b/www/apps/book/app/learn/customization/extend-models/query-linked-records/page.mdx @@ -24,11 +24,11 @@ Query is a utility that retrieves data across modules and their links. It’s re items={[ { text: "Brand Module", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" }, { text: "Defined link between the Brand and Product data models.", - link: "/customization/extend-models/define-link" + link: "/learn/customization/extend-models/define-link" } ]} /> diff --git a/www/apps/book/app/learn/customization/integrate-systems/handle-event/page.mdx b/www/apps/book/app/learn/customization/integrate-systems/handle-event/page.mdx index 3d72c3e258565..d0cad26da6354 100644 --- a/www/apps/book/app/learn/customization/integrate-systems/handle-event/page.mdx +++ b/www/apps/book/app/learn/customization/integrate-systems/handle-event/page.mdx @@ -18,7 +18,7 @@ This chapter covers how to emit an event when a brand is created, listen to that items={[ { text: "Brand Module with createBrandWorkflow", - link: "/customization/custom-features/workflow" + link: "/learn/customization/custom-features/workflow" } ]} /> diff --git a/www/apps/book/app/learn/customization/integrate-systems/schedule-task/page.mdx b/www/apps/book/app/learn/customization/integrate-systems/schedule-task/page.mdx index 375c1b3a00484..58f70e5251bd4 100644 --- a/www/apps/book/app/learn/customization/integrate-systems/schedule-task/page.mdx +++ b/www/apps/book/app/learn/customization/integrate-systems/schedule-task/page.mdx @@ -18,7 +18,7 @@ This chapter covers how to use workflows and scheduled jobs to sync brands from items={[ { text: "Brand Module", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" }, ]} /> diff --git a/www/apps/book/app/learn/customization/integrate-systems/service/page.mdx b/www/apps/book/app/learn/customization/integrate-systems/service/page.mdx index cd79fa6c9d83f..82b96e44a10d7 100644 --- a/www/apps/book/app/learn/customization/integrate-systems/service/page.mdx +++ b/www/apps/book/app/learn/customization/integrate-systems/service/page.mdx @@ -18,7 +18,7 @@ This chapter covers how to integrate a dummy third-party system in a service as items={[ { text: "Brand Module", - link: "/customization/custom-features/module" + link: "/learn/customization/custom-features/module" } ]} /> @@ -192,7 +192,7 @@ module.exports = defineConfig({ apiKey: process.env.BRAND_API_KEY || "temp", }, }, - ] + ], }) ``` diff --git a/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx b/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx index 80fc794f04ca7..57f728efda905 100644 --- a/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx +++ b/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx @@ -12,7 +12,7 @@ In this chapter, you'll learn how to write integration tests for API routes usin items={[ { text: "Testing Tools Setup", - link: "/debugging-and-testing/testing-tools" + link: "/learn/debugging-and-testing/testing-tools" } ]} /> @@ -60,6 +60,8 @@ medusaIntegrationTestRunner({ }) }, }) + +jest.setTimeout(60 * 1000) ``` You use the `medusaIntegrationTestRunner` to write your tests. @@ -86,6 +88,15 @@ If you don't have a `test:integration` script in `package.json`, refer to the [M This runs your Medusa application and runs the tests available under the `src/integrations/http` directory. +### Jest Timeout + +Since your tests connect to the database and perform actions that require more time than the typical tests, make sure to increase the timeout in your test: + +```ts title="integration-tests/http/custom-routes.spec.ts" +// in your test's file +jest.setTimeout(60 * 1000) +``` + --- ## Test a POST API Route diff --git a/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx b/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx index 4a4d5330f6785..c8b0dfe5cd633 100644 --- a/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx +++ b/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx @@ -36,6 +36,8 @@ medusaIntegrationTestRunner({ // TODO write tests... }, }) + +jest.setTimeout(60 * 1000) ``` The `medusaIntegrationTestRunner` function accepts an object as a parameter. The object has a required property `testSuite`. @@ -50,6 +52,15 @@ The `medusaIntegrationTestRunner` function accepts an object as a parameter. The The tests in the `testSuite` function are written using [Jest](https://jestjs.io/). +### Jest Timeout + +Since your tests connect to the database and perform actions that require more time than the typical tests, make sure to increase the timeout in your test: + +```ts title="integration-tests/http/test.spec.ts" +// in your test's file +jest.setTimeout(60 * 1000) +``` + --- ### Run Tests diff --git a/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx b/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx index 0aec5b27c1565..d5cf5bb91b6e6 100644 --- a/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx +++ b/www/apps/book/app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx @@ -12,7 +12,7 @@ In this chapter, you'll learn how to write integration tests for workflows using items={[ { text: "Testing Tools Setup", - link: "/debugging-and-testing/testing-tools" + link: "/learn/debugging-and-testing/testing-tools" } ]} /> @@ -61,10 +61,21 @@ medusaIntegrationTestRunner({ }) }, }) + +jest.setTimeout(60 * 1000) ``` You use the `medusaIntegrationTestRunner` to write an integration test for the workflow. The test pases if the workflow returns the string `"Hello, World!"`. +### Jest Timeout + +Since your tests connect to the database and perform actions that require more time than the typical tests, make sure to increase the timeout in your test: + +```ts title="integration-tests/http/custom-routes.spec.ts" +// in your test's file +jest.setTimeout(60 * 1000) +``` + --- ## Run Test diff --git a/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx b/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx index e88c4811c07a0..c104862d8f516 100644 --- a/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx +++ b/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx @@ -12,7 +12,7 @@ In this chapter, find an example of writing an integration test for a module usi items={[ { text: "Testing Tools Setup", - link: "/debugging-and-testing/testing-tools" + link: "/learn/debugging-and-testing/testing-tools" } ]} /> diff --git a/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/page.mdx b/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/page.mdx index c0229a41b39a0..b265527cca561 100644 --- a/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/page.mdx +++ b/www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/page.mdx @@ -12,7 +12,7 @@ In this chapter, you'll learn about the `moduleIntegrationTestRunner` utility fu items={[ { text: "Testing Tools Setup", - link: "/debugging-and-testing/testing-tools" + link: "/learn/debugging-and-testing/testing-tools" } ]} /> diff --git a/www/apps/book/app/learn/deployment/general/page.mdx b/www/apps/book/app/learn/deployment/general/page.mdx new file mode 100644 index 0000000000000..b77d1d9548816 --- /dev/null +++ b/www/apps/book/app/learn/deployment/general/page.mdx @@ -0,0 +1,331 @@ +import { Prerequisites } from "docs-ui" + +export const metadata = { + title: `General Medusa Application Deployment Guide`, +} + +# {metadata.title} + +In this document, you'll learn the general steps to deploy your Medusa application. How you apply these steps depend on your chosen hosting provider or platform. + + + +Find how-to guides for specific platforms in [this documentation](!resources!/deployment). + + + + + +## Hosting Provider Requirements + +When you deploy your Medusa application, make sure your chosen hosting provider supports deploying the following resources: + +1. PostgreSQL database: If your hosting provider doesn't support database hosting, you must find another hosting provider for the PostgreSQL database. +2. Redis database: If your hosting provider doesn't support database hosting, you must find another hosting provider for the Redis database. +3. Medusa application in server and worker mode. This means your hosting provider should support deploying two applications or instances from the same codebase. +5. If you're also hosting the Medusa Admin with the server, the hosting provider and plan should offer at least 2GB of RAM. + +--- + +## 1. Configure Medusa Application + +### Worker Mode + +The `workerMode` configuration determines which mode the Medusa application runs in. + +When you deploy the Medusa application, you deploy two instances: one in server mode, and one in worker mode. + +Learn more about the `workerMode` configuration in [this document](!resources!/references/medusa-config#workermode). + +So, add the following configuration in `medusa-config.ts`: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + projectConfig: { + // ... + workerMode: process.env.MEDUSA_WORKER_MODE as "shared" | "worker" | "server", + }, +}) +``` + +Later, you’ll set different values of the `MEDUSA_WORKER_MODE` environment variable for each Medusa application deployment or instance. + +### Configure Medusa Admin + +There are two cases where you may disable the Medusa Admin in your deployed Medusa application: + +1. If you choose to host it separately. +2. In the Medusa application running in worker mode, as it doesn’t need to run the admin. + + + +To host the admin with the Medusa application, the hosting provider and plan should offer at least 2GB of RAM. + + + +So, add the following configuration in `medusa-config.ts`: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + admin: { + disable: process.env.DISABLE_MEDUSA_ADMIN === "true", + }, +}) +``` + +Later, you’ll set different values of the `DISABLE_MEDUSA_ADMIN` environment variable. + +### Configure Redis URL + +The `redisUrl` configuration specifies the connection URL to Redis to store the Medusa server's session. + + + +Learn more in the [Medusa Configuration documentation](!resources!/references/medusa-config#redisurl). + + + +So, add the following configuration in `medusa-config.ts` : + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + projectConfig: { + // ... + redisUrl: process.env.REDIS_URL, + }, +}) +``` + +--- + +## 2. Add predeploy Script + +Before you start the Medusa application in production, you should always run migrations and sync links. + +So, add the following script in `package.json`: + +```json +"scripts": { + // ... + "predeploy": "medusa db:migrate" +}, +``` + +--- + +## 3. Install Production Modules and Providers + +By default, your Medusa application uses modules and providers useful for development, such as the In-Memory Cache Module or the Local File Module Provider. + +It’s highly recommended to instead use modules and providers suitable for production, including: + +- [Redis Cache Module](!resources!/architectural-modules/cache/redis) +- [Redis Event Bus Module](!resources!/architectural-modules/event/redis) +- [Workflow Engine Redis Module](!resources!/architectural-modules/workflow-engine/redis) +- [S3 File Module Provider](!resources!/architectural-modules/file/s3) (or other file module providers production-ready). +- [SendGrid Notification Module Provider](!resources!/architectural-modules/notification/sendgrid) (or other notification module providers production-ready). + +Then, add these modules in `medusa-config.ts`: + +```ts title="medusa-config.ts" +import { Modules } from "@medusajs/framework/utils" + +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "@medusajs/medusa/cache-redis", + options: { + redisUrl: process.env.REDIS_URL, + }, + }, + { + resolve: "@medusajs/medusa/event-bus-redis", + options: { + redisUrl: process.env.REDIS_URL, + }, + }, + { + resolve: "@medusajs/medusa/workflow-engine-redis", + options: { + redis: { + url: process.env.REDIS_URL, + }, + }, + }, + ] +}) +``` + + + +Check out the [Integrations](!resources!/integrations) and [Architectural Modules](!resources!/architectural-modules) documentation for other modules and providers to use. + + + +--- + +## 4. Create PostgreSQL and Redis Databases + +Your Medusa application must connect to PostgreSQL and Redis databases. So, before you deploy it, create production PostgreSQL and Redis databases. + +If your hosting provider doesn't support databases, you can use [Neon for PostgreSQL database hosting](https://neon.tech/), and [Redis Cloud for the Redis database hosting](https://redis.io/cloud/). + +After hosting both databases, keep their connection URLs for the next steps. + +--- + +## 5. Deploy Medusa Application in Server Mode + +As mentioned earlier, you'll deploy two instances or create two deployments of your Medusa application: one in server mode, and the other in worker mode. + +The deployment steps depend on your hosting provider. This section provides the general steps to perform during the deployment. + +### Set Environment Variables + +When setting the environment variables of the Medusa application, set the following variables: + +```bash +COOKIE_SECRET=supersecret # TODO GENERATE SECURE SECRET +JWT_SECRET=supersecret # TODO GENERATE SECURE SECRET +STORE_CORS= # STOREFRONT URL +ADMIN_CORS= # ADMIN URL +AUTH_CORS= # STOREFRONT AND ADMIN URLS, SEPARATED BY COMMAS +# change to false if you're hosting the admin with the application +DISABLE_MEDUSA_ADMIN=true +MEDUSA_WORKER_MODE=server +PORT=9000 +DATABASE_URL # POSTGRES DATABASE URL +REDIS_URL= # REDIS DATABASE URL +``` + +Where: + +- The value of `COOKIE_SECRET` and `JWT_SECRET` must be a randomly generated secret. +- `STORE_CORS`'s value is the URL of your storefront. If you don’t have it yet, you can skip adding it for now. +- `ADMIN_CORS`'s value is the URL of the admin dashboard. If you don’t have it yet, or you’re deploying the admin with the Medusa application, you can skip adding it for now. +- `AUTH_CORS`'s value is the URLs of any application authenticating users, customers, or other actor types, such as the storefront and admin URLs. The URLs are separated by commas. If you don’t have the URLs yet, you can set its value later. +- Change `DISABLE_MEDUSA_ADMIN` to `false` if you’re hosting the admin with the Medusa application. +- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL` +- Set the Redis database's connection URL as the value of `REDIS_URL`. + +Feel free to add any other relevant environment variables, such as for integrations and architectural modules. + +### Set Start Command + +The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. + +So, you must run the `start` command from the `.medusa/server` directory. + +If your hosting provider doesn't support setting a current-working directory, set the start command to the following: + +```bash npm2yarn +cd .medusa/server && npm run predeploy && npm run start +``` + +### Additional Configuration if Deploying with Admin + +If you’re deploying the Medusa application in server mode with the admin, you have to make some changes after you’ve obtained the application’s URL. + +First, add the following configuration to `medusa-config.ts`: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + admin: { + // ... + backendUrl: process.env.MEDUSA_BACKEND_URL, + }, +}) +``` + +Then, push the changes to the GitHub repository or deployed application. + +In your hosting provider, add or modify the following environment variables for the Medusa application in server mode: + +```bash +ADMIN_CORS= # MEDUSA APPLICATION URL +AUTH_CORS= # ADD MEDUSA APPLICATION URL +MEDUSA_BACKEND_URL= # URL TO DEPLOYED MEDUSA APPLICATION +``` + +Where you set the value of `ADMIN_CORS` and `MEDUSA_BACKEND_URL` to the Medusa application’s URL, and you add the URL to `AUTH_CORS`. + + + +Remember to separate URLs in `AUTH_CORS` by commas. + + + +--- + +## 6. Deploy Medusa Application in Worker Mode + +Next, you'll deploy the Medusa application in worker mode. + +As explained in the previous section, the deployment steps depend on your hosting provider. This section provides the general steps to perform during the deployment. + +### Set Environment Variables + +When setting the environment variables of the Medusa application, set the following variables: + +```bash +COOKIE_SECRET=supersecret # TODO GENERATE SECURE SECRET +JWT_SECRET=supersecret # TODO GENERATE SECURE SECRET +DISABLE_MEDUSA_ADMIN=true +MEDUSA_WORKER_MODE=worker +PORT=9000 +DATABASE_URL # POSTGRES DATABASE URL +REDIS_URL= # REDIS DATABASE URL +``` + +Where: + +- The value of `COOKIE_SECRET` and `JWT_SECRET` must be a randomly generated secret. +- Keep `DISABLE_MEDUSA_ADMIN`'s value set to `true`, even if you’re hosting the admin with the Medusa application. +- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL` +- Set the Redis database's connection URL as the value of `REDIS_URL`. + +Feel free to add any other relevant environment variables, such as for integrations and architectural modules. + +### Set Start Command + +The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. + +So, you must run the `start` command from the `.medusa/server` directory. + +If your hosting provider doesn't support setting a current-working directory, set the start command to the following: + +```bash npm2yarn +cd .medusa/server && npm run predeploy && npm run start +``` + +--- + +## 7. Test Deployed Application + +Once the application is deployed and live, go to `/health`, where `` is the URL of the Medusa application in server mode. If the deployment was successful, you’ll see the `OK` response. + +### Open Deployed Medusa Admin + +If you deployed the Medusa Admin with the application, it’ll be available at `/app`. + +--- + +## Create Admin User + +If your hosting provider supports running commands in your Medusa application's directory, run the following command to create an admin user: + +```bash +npx medusa user -e admin-medusa@test.com -p supersecret +``` + +Replace the email `admin-medusa@test.com` and password `supersecret` with the credentials you want. + +You can use these credentials to log into the Medusa Admin dashboard. diff --git a/www/apps/book/app/learn/deployment/page.mdx b/www/apps/book/app/learn/deployment/page.mdx index 4c91773771786..0e8f161f1ff0b 100644 --- a/www/apps/book/app/learn/deployment/page.mdx +++ b/www/apps/book/app/learn/deployment/page.mdx @@ -1,5 +1,5 @@ export const metadata = { - title: `${pageNumber} Deployment Overview`, + title: `${pageNumber} Medusa Deployment Overview`, } # {metadata.title} @@ -12,7 +12,7 @@ A standard Medusa project is made up of: - Medusa application: The Medusa server and the Medusa Admin. - One or more storefronts - + ![Diagram showcasing the connection between the three deployed components](https://res.cloudinary.com/dza7lstvk/image/upload/v1708600807/Medusa%20Book/deployment-options_ceuuvo.jpg) You can either deploy the Medusa application fully (server with the admin), or deploy the Medusa Admin separately. The storefront is always deployed separately. @@ -35,7 +35,11 @@ For optimal experience, make sure that the hosting provider and plan offer at le Your server connects to a PostgreSQL database, Redis, and other services relevant for your setup. Most hosting providers support deploying and managing these databases along with your Medusa server (such as Railway and DigitalOcean). -Refer to [this reference](!resources!/deployment) to find how-to deployment guides for specific hosting providers. +### How to Deploy Medusa? + +The [next chapter](./general/page.mdx) explains the general steps to deploy your Medusa application. + +Refer to [this reference](!resources!/deployment) to find how-to deployment guides for general and specific hosting providers. --- @@ -51,7 +55,7 @@ In this scenario, make sure the hosting provider and plan of your choice provide -The server deployment guides mention details on how to deploy the admin with the Medusa server. +The [server deployment guides](!resources!/deployment) mention details on how to deploy the admin with the Medusa server. diff --git a/www/apps/book/app/learn/installation/page.mdx b/www/apps/book/app/learn/installation/page.mdx index afde8da56d4a1..e64287b8627a2 100644 --- a/www/apps/book/app/learn/installation/page.mdx +++ b/www/apps/book/app/learn/installation/page.mdx @@ -109,7 +109,7 @@ You can then use the user's credentials to log into the Medusa Admin application By default, your Medusa application is equipped with the basic configuration to start your development. -If you run into issues with configurations, such as CORS configurations, or need to make changes to the default configuration, refer to [this guide on all available configurations](!resources!/medusa-config). +If you run into issues with configurations, such as CORS configurations, or need to make changes to the default configuration, refer to [this guide on all available configurations](!resources!/references/medusa-config). --- diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs index 1fb70dd2e8b3c..1b0f767355a7c 100644 --- a/www/apps/book/generated/edit-dates.mjs +++ b/www/apps/book/generated/edit-dates.mjs @@ -1,7 +1,7 @@ export const generatedEditDates = { "app/learn/basics/scheduled-jobs/page.mdx": "2024-09-30T08:43:53.132Z", "app/learn/basics/workflows/page.mdx": "2024-09-30T08:43:53.132Z", - "app/learn/deployment/page.mdx": "2024-08-05T07:24:05+00:00", + "app/learn/deployment/page.mdx": "2024-11-11T11:03:59.725Z", "app/learn/page.mdx": "2024-09-03T07:09:09.034Z", "app/learn/basics/commerce-modules/page.mdx": "2024-09-30T08:43:53.131Z", "app/learn/advanced-development/workflows/retry-failed-steps/page.mdx": "2024-09-30T08:43:53.130Z", @@ -35,7 +35,7 @@ export const generatedEditDates = { "app/learn/advanced-development/events-and-subscribers/data-payload/page.mdx": "2024-07-16T17:12:05+01:00", "app/learn/advanced-development/data-models/default-properties/page.mdx": "2024-09-19T07:32:06.118Z", "app/learn/advanced-development/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z", - "app/learn/advanced-development/events-and-subscribers/emit-event/page.mdx": "2024-09-30T08:43:53.125Z", + "app/learn/advanced-development/events-and-subscribers/emit-event/page.mdx": "2024-11-11T15:17:58.045Z", "app/learn/advanced-development/workflows/conditions/page.mdx": "2024-09-30T08:43:53.128Z", "app/learn/advanced-development/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00", "app/learn/advanced-development/admin/page.mdx": "2024-10-07T12:39:13.178Z", @@ -52,7 +52,7 @@ export const generatedEditDates = { "app/learn/advanced-development/modules/module-links/page.mdx": "2024-09-30T08:43:53.126Z", "app/learn/advanced-development/data-models/searchable-property/page.mdx": "2024-09-30T08:43:53.125Z", "app/learn/advanced-development/scheduled-jobs/execution-number/page.mdx": "2024-07-02T09:41:15+00:00", - "app/learn/advanced-development/api-routes/parameters/page.mdx": "2024-09-11T10:44:13.491Z", + "app/learn/advanced-development/api-routes/parameters/page.mdx": "2024-11-12T13:35:09.393Z", "app/learn/advanced-development/api-routes/http-methods/page.mdx": "2024-09-11T10:43:33.169Z", "app/learn/advanced-development/admin/tips/page.mdx": "2024-10-07T12:50:36.335Z", "app/learn/advanced-development/api-routes/cors/page.mdx": "2024-09-30T08:43:53.121Z", @@ -72,7 +72,7 @@ export const generatedEditDates = { "app/learn/advanced-development/modules/service-constraints/page.mdx": "2024-09-30T08:43:53.127Z", "app/learn/advanced-development/api-routes/page.mdx": "2024-09-04T09:36:33.961Z", "app/learn/advanced-development/api-routes/responses/page.mdx": "2024-09-11T10:44:37.016Z", - "app/learn/advanced-development/api-routes/validation/page.mdx": "2024-09-11T10:46:31.476Z", + "app/learn/advanced-development/api-routes/validation/page.mdx": "2024-11-12T13:32:32.484Z", "app/learn/advanced-development/api-routes/errors/page.mdx": "2024-09-30T08:43:53.121Z", "app/learn/advanced-development/admin/constraints/page.mdx": "2024-09-10T11:39:51.165Z", "app/learn/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx": "2024-10-16T08:50:03.061Z", @@ -80,7 +80,7 @@ export const generatedEditDates = { "app/learn/advanced-development/module-links/custom-columns/page.mdx": "2024-09-16T15:51:33.570Z", "app/learn/advanced-development/module-links/directions/page.mdx": "2024-09-16T15:37:51.441Z", "app/learn/advanced-development/module-links/page.mdx": "2024-09-16T15:36:48.190Z", - "app/learn/advanced-development/module-links/query/page.mdx": "2024-09-16T12:42:27.579Z", + "app/learn/advanced-development/module-links/query/page.mdx": "2024-11-12T15:40:24.411Z", "app/learn/advanced-development/module-links/remote-link/page.mdx": "2024-09-16T12:42:27.581Z", "app/learn/advanced-development/modules/db-operations/page.mdx": "2024-09-16T14:38:29.150Z", "app/learn/advanced-development/modules/multiple-services/page.mdx": "2024-09-16T14:41:32.975Z", @@ -113,5 +113,8 @@ export const generatedEditDates = { "app/learn/advanced-development/data-models/infer-type/page.mdx": "2024-09-30T08:43:53.123Z", "app/learn/advanced-development/custom-cli-scripts/seed-data/page.mdx": "2024-10-03T11:11:07.181Z", "app/learn/basics/modules/page.mdx": "2024-10-16T08:49:39.997Z", - "app/learn/advanced-development/environment-variables/page.mdx": "2024-10-25T14:59:07.831Z" + "app/learn/advanced-development/environment-variables/page.mdx": "2024-10-25T14:59:07.831Z", + "app/learn/build/page.mdx": "2024-11-11T11:08:41.832Z", + "app/learn/deployment/general/page.mdx": "2024-11-11T11:50:04.248Z", + "app/learn/advanced-development/workflows/multiple-step-usage/page.mdx": "2024-11-12T11:11:49.191Z" } \ No newline at end of file diff --git a/www/apps/book/sidebar.mjs b/www/apps/book/sidebar.mjs index 757ff0328313f..af6d52d3ec39d 100644 --- a/www/apps/book/sidebar.mjs +++ b/www/apps/book/sidebar.mjs @@ -392,8 +392,9 @@ export const sidebar = numberSidebarItems( title: "Events Data Payload", }, { + type: "link", path: "/learn/advanced-development/events-and-subscribers/emit-event", - title: "Emit an Event", + title: "Emit Event", }, ], }, @@ -473,6 +474,11 @@ export const sidebar = numberSidebarItems( path: "/learn/advanced-development/workflows/execute-another-workflow", title: "Execute Another Workflow", }, + { + type: "link", + path: "/learn/advanced-development/workflows/multiple-step-usage", + title: "Multiple Step Usage", + }, ], }, { @@ -588,8 +594,21 @@ export const sidebar = numberSidebarItems( }, { type: "link", - path: "/learn/deployment", - title: "Deployment", + path: "/learn/build", + title: "Build", + chapterTitle: "Production", + children: [ + { + type: "link", + path: "/learn/deployment", + title: "Deployment Overview", + }, + { + type: "link", + path: "/learn/deployment/general", + title: "General Deployment", + } + ] }, { type: "link", diff --git a/www/apps/resources/app/admin-widget-injection-zones/page.mdx b/www/apps/resources/app/admin-widget-injection-zones/page.mdx index 63f2cd91b732c..4d61b618f85a9 100644 --- a/www/apps/resources/app/admin-widget-injection-zones/page.mdx +++ b/www/apps/resources/app/admin-widget-injection-zones/page.mdx @@ -118,7 +118,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Order object + data, // AdminOrder object } ``` @@ -141,7 +141,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Order object + data, // AdminOrder object } ``` @@ -164,7 +164,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Order object + data, // AdminOrder object } ``` @@ -187,7 +187,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Order object + data, // AdminOrder object } ``` @@ -258,7 +258,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Customer object + data, // AdminCustomer object } ``` @@ -281,7 +281,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Customer object + data, // AdminCustomer object } ``` @@ -338,7 +338,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // CustomerGroup object + data, // AdminCustomerGroup object } ``` @@ -361,7 +361,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // CustomerGroup object + data, // AdminCustomerGroup object } ``` @@ -432,7 +432,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Product object + data, // AdminProduct object } ``` @@ -455,7 +455,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Product object + data, // AdminProduct object } ``` @@ -478,7 +478,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Product object + data, // AdminProduct object } ``` @@ -501,7 +501,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Product object + data, // AdminProduct object } ``` @@ -558,7 +558,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // ProductCollection object + data, // AdminProductCollection object } ``` @@ -581,7 +581,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // ProductCollection object + data, // AdminProductCollection object } ``` @@ -638,7 +638,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // ProductCategory object + data, // AdminProductCategory object } ``` @@ -661,7 +661,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // ProductCategory object + data, // AdminProductCategory object } ``` @@ -684,7 +684,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // ProductCategory object + data, // AdminProductCategory object } ``` @@ -707,7 +707,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // ProductCategory object + data, // AdminProductCategory object } ``` @@ -774,11 +774,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // PriceList object + data, // AdminPriceList object } ``` @@ -797,11 +797,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // PriceList object + data, // AdminPriceList object } ``` @@ -820,11 +820,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // PriceList object + data, // AdminPriceList object } ``` @@ -843,11 +843,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // PriceList object + data, // AdminPriceList object } ``` @@ -914,11 +914,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Promotion object + data, // AdminPromotion object } ``` @@ -937,11 +937,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Promotion object + data, // AdminPromotion object } ``` @@ -960,11 +960,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Promotion object + data, // AdminPromotion object } ``` @@ -983,11 +983,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Promotion object + data, // AdminPromotion object } ``` @@ -1040,11 +1040,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Campaign object + data, // AdminCampaign object } ``` @@ -1063,11 +1063,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Campaign object + data, // AdminCampaign object } ``` @@ -1086,11 +1086,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Campaign object + data, // AdminCampaign object } ``` @@ -1109,11 +1109,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Campaign object + data, // AdminCampaign object } ``` @@ -1182,11 +1182,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // User object + data, // AdminUser object } ``` @@ -1205,11 +1205,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // User object + data, // AdminUser object } ``` @@ -1242,11 +1242,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Store object + data, // AdminStore object } ``` @@ -1265,11 +1265,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // Store object + data, // AdminStore object } ``` @@ -1302,11 +1302,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // User object + data, // AdminUser object } ``` @@ -1325,11 +1325,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // User object + data, // AdminUser object } ``` @@ -1400,7 +1400,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Region object + data, // AdminRegion object } ``` @@ -1423,7 +1423,7 @@ This documentation page includes the list of injection zones you can add Admin W ```ts blockStyle="inline" { - data, // Region object + data, // AdminRegion object } ``` @@ -1490,11 +1490,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ShippingProfile object + data, // AdminShippingProfile object } ``` @@ -1513,11 +1513,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ShippingProfile object + data, // AdminShippingProfile object } ``` @@ -1584,11 +1584,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // StockLocation object + data, // AdminStockLocation object } ``` @@ -1607,11 +1607,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // StockLocation object + data, // AdminStockLocation object } ``` @@ -1630,11 +1630,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // StockLocation object + data, // AdminStockLocation object } ``` @@ -1653,11 +1653,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // StockLocation object + data, // AdminStockLocation object } ``` @@ -1724,11 +1724,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // SalesChannel object + data, // AdminSalesChannel object } ``` @@ -1747,11 +1747,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // SalesChannel object + data, // AdminSalesChannel object } ``` @@ -1818,11 +1818,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ReservationItem object + data, // AdminReservation object } ``` @@ -1841,11 +1841,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ReservationItem object + data, // AdminReservation object } ``` @@ -1864,11 +1864,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ReservationItem object + data, // AdminReservation object } ``` @@ -1887,11 +1887,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ReservationItem object + data, // AdminReservation object } ``` @@ -1958,11 +1958,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ApiKey object + data, // AdminApiKey object } ``` @@ -1981,11 +1981,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // ApiKey object + data, // AdminApiKey object } ``` @@ -2052,11 +2052,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // WorkflowExecution object + data, // AdminWorkflowExecution object } ``` @@ -2075,11 +2075,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // WorkflowExecution object + data, // AdminWorkflowExecution object } ``` @@ -2146,11 +2146,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // TaxRegion object + data, // AdminTaxRegion object } ``` @@ -2169,11 +2169,11 @@ This documentation page includes the list of injection zones you can add Admin W - Type `DetailWidgetProps` imported from `@medusajs/framework/types` + Type `DetailWidgetProps` imported from `@medusajs/framework/types` ```ts blockStyle="inline" { - data, // TaxRegion object + data, // AdminTaxRegion object } ``` diff --git a/www/apps/resources/app/architectural-modules/event/create/page.mdx b/www/apps/resources/app/architectural-modules/event/create/page.mdx index 9344ee3f490fd..0f47f55d33757 100644 --- a/www/apps/resources/app/architectural-modules/event/create/page.mdx +++ b/www/apps/resources/app/architectural-modules/event/create/page.mdx @@ -1,3 +1,5 @@ +import { TypeList } from "docs-ui" + export const metadata = { title: `How to Create an Event Module`, } @@ -19,15 +21,18 @@ Create the file `src/modules/my-event/service.ts` that holds the implementation The Event Module's main service must extend the `AbstractEventBusModuleService` class imported from `@medusajs/framework/utils`: ```ts title="src/modules/my-event/service.ts" -import { EmitData, Message } from "@medusajs/framework/types" -import { AbstractEventBusModuleService } from "@medusajs/framework/utils" +import { AbstractEventBusModuleService } from "@medusajs/framework/utils"; +import { Message } from "@medusajs/types"; class MyEventService extends AbstractEventBusModuleService { - emit(eventName: string, data: T, options: Record): Promise; - emit(data: EmitData[]): Promise; - emit(data: Message[]): Promise; - emit(eventName: unknown, data?: unknown, options?: unknown): Promise { - throw new Error("Method not implemented.") + async emit(data: Message | Message[], options: Record): Promise { + throw new Error("Method not implemented."); + } + async releaseGroupedEvents(eventGroupId: string): Promise { + throw new Error("Method not implemented."); + } + async clearGroupedEvents(eventGroupId: string): Promise { + throw new Error("Method not implemented."); } } @@ -36,52 +41,175 @@ export default MyEventService The service implements the required methods based on the desired publish/subscribe logic. -### Note About the eventToSubscribersMap Property +### eventToSubscribersMap_ Property -The `AbstractEventBusModuleService` has a field `eventToSubscribersMap`, which is a JavaScript Map. The map's keys are the event names, whereas the value of each key is an array of subscribed handler functions. +The `AbstractEventBusModuleService` has a field `eventToSubscribersMap_`, which is a JavaScript Map. The map's keys are the event names, whereas the value of each key is an array of subscribed handler functions. In your custom implementation, you can use this property to manage the subscribed handler functions: ```ts const eventSubscribers = - this.eventToSubscribersMap.get(eventName) || [] + this.eventToSubscribersMap_.get(eventName) || [] ``` -### Implement emit Method +### emit Method The `emit` method is used to push an event from the Medusa application into your messaging system. The subscribers to that event would then pick up the message and execute their asynchronous tasks. -The `emit` method has three different signatures: +An example implementation: + +```ts title="src/modules/my-event/service.ts" +class MyEventService extends AbstractEventBusModuleService { + async emit(data: Message | Message[], options: Record): Promise { + const events = Array.isArray(data) ? data : [data] + + for (const event of events) { + console.log(`Received the event ${event.name} with data ${event.data}`) + + // TODO push the event somewhere + } + } + // ... +} +``` + +The `emit` method receives the following parameters: + + + +### releaseGroupedEvents Method + +Grouped events are useful when you have distributed transactions where you need to explicitly group, release, and clear events upon lifecycle transaction events. + +If your Event Module supports grouped events, this method is used to emit all events in a group, then clear that group. + +For example: + +```ts title="src/modules/my-event/service.ts" +class MyEventService extends AbstractEventBusModuleService { + protected groupedEventsMap_: Map + + constructor() { + // @ts-ignore + super(...arguments) + + this.groupedEventsMap_ = new Map() + } + + async releaseGroupedEvents(eventGroupId: string): Promise { + const groupedEvents = this.groupedEventsMap_.get(eventGroupId) || [] + + for (const event of groupedEvents) { + const { options, ...eventBody } = event + + // TODO emit event + } -1. The first signature accepts three parameters: - - The first parameter is a string indicating the name of the event to trigger. - - The second parameter is data to send to subscribers of that event. - - The third optional parameter can be used to pass options specific to the event service. -2. The second signature accepts one parameter, which is an array of objects having the following properties: - - `eventName`: A string indicating the name of the event to trigger. - - `data`: The data to send to subsribers of that event. - - `options`: (optional) options specific to the event service. -3. The third signature accepts one parameter, which is an array of objects having the following properties: - - `eventName`: A string indicating the name of the event to trigger. - - `body`: An object of event-related data. It has two properties: `data` holding the data of the event, and `metadata` which is an object with more details on how the event was emitted, such as the action that occurred or the service that emitted it. - - `options`: (optional) options specific to the event service. + await this.clearGroupedEvents(eventGroupId) + } + + // ... +} +``` + +The `releaseGroupedEvents` receives the group ID as a parameter. + +In the example above, you add a `groupedEventsMap_` property to store grouped events. Then, in the method, you emit the events in the group, then clear the grouped events using the `clearGroupedEvents` which you'll learn about next. -You can implement your method in a way that supports both signatures by checking the type of the first input. For example: +To add events to the grouped events map, you can do it in the `emit` method: ```ts title="src/modules/my-event/service.ts" class MyEventService extends AbstractEventBusModuleService { // ... - emit(eventName: string, data: T, options: Record): Promise; - emit(data: EmitData[]): Promise; - emit(data: Message[]): Promise; - emit(eventOrData: unknown, data?: unknown, options?: unknown): Promise { - const isBulkEmit = Array.isArray(eventOrData) + async emit(data: Message | Message[], options: Record): Promise { + const events = Array.isArray(data) ? data : [data] + + for (const event of events) { + console.log(`Received the event ${event.name} with data ${event.data}`) + + if (event.metadata.eventGroupId) { + const groupedEvents = this.groupedEventsMap_.get( + event.metadata.eventGroupId + ) || [] + + groupedEvents.push(event) + + this.groupedEventsMap_.set(event.metadata.eventGroupId, groupedEvents) + continue + } + + // TODO push the event somewhere + } + } +} +``` + +### clearGroupedEvents Method + +If your Event Module supports grouped events, this method is used to remove the events of a group. - // ... +For example: + +```ts title="src/modules/my-event/service.ts" +class MyEventService extends AbstractEventBusModuleService { + // from previous section + protected groupedEventsMap_: Map + + async clearGroupedEvents(eventGroupId: string): Promise { + this.groupedEventsMap_.delete(eventGroupId) } + + // ... } ``` +The method accepts the group's name as a parameter. + +In the method, you delete the group from the `groupedEventsMap_` property (added in the previous section), deleting the stored events of it as well. + --- ## 3. Create Module Definition File diff --git a/www/apps/resources/app/architectural-modules/page.mdx b/www/apps/resources/app/architectural-modules/page.mdx index 86a3546e46838..ae19437da66dc 100644 --- a/www/apps/resources/app/architectural-modules/page.mdx +++ b/www/apps/resources/app/architectural-modules/page.mdx @@ -8,4 +8,4 @@ export const metadata = { This section includes documentation for official Medusa architectural modules. - \ No newline at end of file + \ No newline at end of file diff --git a/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx b/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx index b37100870fb29..63fe239709fac 100644 --- a/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx +++ b/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx @@ -349,7 +349,7 @@ You add a step that deletes the manager using the `deleteManagers` method of the Next, in the same file, add the workflow that deletes a manager: export const deleteHighlights = [ - ["30", "manager_id", "If your actor type has a different name, such as vendor, change it to be `{actor_type}_id`."] + ["29", "manager_id", "If your actor type has a different name, such as vendor, change it to be `{actor_type}_id`."] ] ```ts title="src/workflows/delete-manager.ts" collapsibleLines="1-15" expandButtonLabel="Show Imports" highlights={deleteHighlights} @@ -363,7 +363,7 @@ import { } from "@medusajs/framework/workflows-sdk" import { setAuthAppMetadataStep, - useRemoteQueryStep, + useQueryGraphStep, } from "@medusajs/medusa/core-flows" // ... @@ -375,17 +375,15 @@ export const deleteManagerWorkflow = createWorkflow( ): WorkflowResponse => { deleteManagerStep(input) - const authIdentities = useRemoteQueryStep({ - entry_point: "auth_identity", + const { data: authIdentities } = useQueryGraphStep({ + entity: "auth_identity", fields: ["id"], - variables: { - filters: { - app_metadata: { - // the ID is of the format `{actor_type}_id`. - manager_id: input.id, - }, - }, - }, + filters: { + app_metadata: { + // the ID is of the format `{actor_type}_id`. + manager_id: input.id, + } + } }) const authIdentity = transform( diff --git a/www/apps/resources/app/commerce-modules/cart/extend/page.mdx b/www/apps/resources/app/commerce-modules/cart/extend/page.mdx index 61875693c96ff..9fd7028e23e08 100644 --- a/www/apps/resources/app/commerce-modules/cart/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/cart/extend/page.mdx @@ -492,7 +492,7 @@ Finally, you'll create the workflow. Create the file `src/workflows/update-custo ```ts title="src/workflows/update-custom-from-cart/index.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" import { CartDTO } from "@medusajs/framework/types" import { createWorkflow, when, WorkflowResponse } from "@medusajs/framework/workflows-sdk" -import { createRemoteLinkStep, dismissRemoteLinkStep, useRemoteQueryStep } from "@medusajs/medusa/core-flows" +import { createRemoteLinkStep, dismissRemoteLinkStep, useQueryGraphStep } from "@medusajs/medusa/core-flows" import { createCustomStep } from "../create-custom-from-cart/steps/create-custom" import { Modules } from "@medusajs/framework/utils" import { HELLO_MODULE } from "../../modules/hello" @@ -509,15 +509,12 @@ export type UpdateCustomFromCartStepInput = { export const updateCustomFromCartWorkflow = createWorkflow( "update-custom-from-cart", (input: UpdateCustomFromCartStepInput) => { - const cartData = useRemoteQueryStep({ - entry_point: "cart", + const { data: carts } = useQueryGraphStep({ + entity: "cart", fields: ["custom.*"], - variables: { - filters: { - id: input.cart.id, - }, - }, - list: false, + filters: { + id: input.cart.id + } }) // TODO create, update, or delete Custom record @@ -534,9 +531,9 @@ Next, replace the `TODO` with the following: ```ts title="src/workflows/update-custom-from-cart/index.ts" const created = when({ input, - cartData, + carts, }, (data) => - !data.cartData.custom && + !data.carts[0].custom && data.input.additional_data?.custom_name?.length > 0 ) .then(() => { @@ -568,25 +565,25 @@ Next, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-cart/index.ts" const deleted = when({ input, - cartData, + carts, }, (data) => - data.cartData.custom && ( + data.carts[0].custom && ( data.input.additional_data?.custom_name === null || data.input.additional_data?.custom_name.length === 0 ) ) .then(() => { deleteCustomStep({ - custom: cartData.custom, + custom: carts[0].custom, }) dismissRemoteLinkStep({ [HELLO_MODULE]: { - custom_id: cartData.custom.id, + custom_id: carts[0].custom.id, }, }) - return cartData.custom.id + return carts[0].custom.id }) // TODO delete Custom record @@ -599,11 +596,11 @@ Finally, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-cart/index.ts" const updated = when({ input, - cartData, -}, (data) => data.cartData.custom && data.input.additional_data?.custom_name?.length > 0) + carts, +}, (data) => data.carts[0].custom && data.input.additional_data?.custom_name?.length > 0) .then(() => { const custom = updateCustomStep({ - id: cartData.custom.id, + id: carts[0].custom.id, custom_name: input.additional_data.custom_name, }) diff --git a/www/apps/resources/app/commerce-modules/customer/extend/page.mdx b/www/apps/resources/app/commerce-modules/customer/extend/page.mdx index a89437efa78a2..dbcc3b5c09c1c 100644 --- a/www/apps/resources/app/commerce-modules/customer/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/customer/extend/page.mdx @@ -504,7 +504,7 @@ Finally, you'll create the workflow. Create the file `src/workflows/update-custo ```ts title="src/workflows/update-custom-from-customer/index.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" import { CustomerDTO } from "@medusajs/framework/types" import { createWorkflow, when, WorkflowResponse } from "@medusajs/framework/workflows-sdk" -import { createRemoteLinkStep, dismissRemoteLinkStep, useRemoteQueryStep } from "@medusajs/medusa/core-flows" +import { createRemoteLinkStep, dismissRemoteLinkStep, useQueryGraphStep } from "@medusajs/medusa/core-flows" import { createCustomStep } from "../create-custom-from-customer/steps/create-custom" import { Modules } from "@medusajs/framework/utils" import { HELLO_MODULE } from "../../modules/hello" @@ -521,15 +521,12 @@ export type UpdateCustomFromCustomerStepInput = { export const updateCustomFromCustomerWorkflow = createWorkflow( "update-custom-from-customer", (input: UpdateCustomFromCustomerStepInput) => { - const customerData = useRemoteQueryStep({ - entry_point: "customer", + const { data: customers } = useQueryGraphStep({ + entity: "customer", fields: ["custom.*"], - variables: { - filters: { - id: input.customer.id, - }, - }, - list: false, + filters: { + id: input.customer.id, + } }) // TODO create, update, or delete Custom record @@ -546,9 +543,9 @@ Next, replace the `TODO` with the following: ```ts title="src/workflows/update-custom-from-customer/index.ts" const created = when({ input, - customerData, + customers, }, (data) => - !data.customerData.custom && + !data.customers[0].custom && data.input.additional_data?.custom_name?.length > 0 ) .then(() => { @@ -580,25 +577,25 @@ Next, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-customer/index.ts" const deleted = when({ input, - customerData, + customers, }, (data) => - data.customerData.custom && ( + data.customers[0].custom && ( data.input.additional_data?.custom_name === null || data.input.additional_data?.custom_name.length === 0 ) ) .then(() => { deleteCustomStep({ - custom: customerData.custom, + custom: customers[0].custom, }) dismissRemoteLinkStep({ [HELLO_MODULE]: { - custom_id: customerData.custom.id, + custom_id: customers[0].custom.id, }, }) - return customerData.custom.id + return customers[0].custom.id }) // TODO delete Custom record @@ -611,11 +608,11 @@ Finally, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-customer/index.ts" const updated = when({ input, - customerData, -}, (data) => data.customerData.custom && data.input.additional_data?.custom_name?.length > 0) + customers, +}, (data) => data.customers[0].custom && data.input.additional_data?.custom_name?.length > 0) .then(() => { const custom = updateCustomStep({ - id: customerData.custom.id, + id: customers[0].custom.id, custom_name: input.additional_data.custom_name, }) diff --git a/www/apps/resources/app/commerce-modules/product/extend/page.mdx b/www/apps/resources/app/commerce-modules/product/extend/page.mdx index 53da450d2fd66..8980bda480ca1 100644 --- a/www/apps/resources/app/commerce-modules/product/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/product/extend/page.mdx @@ -510,7 +510,7 @@ Finally, you'll create the workflow. Create the file `src/workflows/update-custo ```ts title="src/workflows/update-custom-from-product/index.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" import { ProductDTO } from "@medusajs/framework/types" import { createWorkflow, when, WorkflowResponse } from "@medusajs/framework/workflows-sdk" -import { createRemoteLinkStep, dismissRemoteLinkStep, useRemoteQueryStep } from "@medusajs/medusa/core-flows" +import { createRemoteLinkStep, dismissRemoteLinkStep, useQueryGraphStep } from "@medusajs/medusa/core-flows" import { createCustomStep } from "../create-custom-from-cart/steps/create-custom" import { Modules } from "@medusajs/framework/utils" import { HELLO_MODULE } from "../../modules/hello" @@ -527,15 +527,12 @@ export type UpdateCustomFromProductStepInput = { export const updateCustomFromProductWorkflow = createWorkflow( "update-custom-from-product", (input: UpdateCustomFromProductStepInput) => { - const productData = useRemoteQueryStep({ - entry_point: "product", + const { data: products } = useQueryGraphStep({ + entity: "product", fields: ["custom.*"], - variables: { - filters: { - id: input.product.id - } - }, - list: false + filters: { + id: input.product.id, + } }) // TODO create, update, or delete Custom record @@ -552,9 +549,9 @@ Next, replace the `TODO` with the following: ```ts title="src/workflows/update-custom-from-product/index.ts" const created = when({ input, - productData + products }, (data) => - !data.productData.custom && + !data.products[0].custom && data.input.additional_data?.custom_name?.length > 0 ) .then(() => { @@ -586,25 +583,25 @@ Next, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-product/index.ts" const deleted = when({ input, - productData + products }, (data) => - data.productData.custom && ( + data.products[0].custom && ( data.input.additional_data?.custom_name === null || data.input.additional_data?.custom_name.length === 0 ) ) .then(() => { deleteCustomStep({ - custom: productData.custom + custom: products[0].custom }) dismissRemoteLinkStep({ [HELLO_MODULE]: { - custom_id: productData.custom.id + custom_id: products[0].custom.id } }) - return productData.custom.id + return products[0].custom.id }) // TODO delete Custom record @@ -617,11 +614,11 @@ Finally, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-product/index.ts" const updated = when({ input, - productData -}, (data) => data.productData.custom && data.input.additional_data?.custom_name?.length > 0) + products +}, (data) => data.products[0].custom && data.input.additional_data?.custom_name?.length > 0) .then(() => { const custom = updateCustomStep({ - id: productData.custom.id, + id: products[0].custom.id, custom_name: input.additional_data.custom_name }) diff --git a/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx b/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx index 85a81136e3839..488c9b66913fe 100644 --- a/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx @@ -516,7 +516,7 @@ Finally, you'll create the workflow. Create the file `src/workflows/update-custo ```ts title="src/workflows/update-custom-from-promotion/index.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" import { PromotionDTO } from "@medusajs/framework/types" import { createWorkflow, when, WorkflowResponse } from "@medusajs/framework/workflows-sdk" -import { createRemoteLinkStep, dismissRemoteLinkStep, useRemoteQueryStep } from "@medusajs/medusa/core-flows" +import { createRemoteLinkStep, dismissRemoteLinkStep, useQueryGraphStep } from "@medusajs/medusa/core-flows" import { createCustomStep } from "../create-custom-from-cart/steps/create-custom" import { Modules } from "@medusajs/framework/utils" import { HELLO_MODULE } from "../../modules/hello" @@ -533,15 +533,12 @@ export type UpdateCustomFromPromotionStepInput = { export const updateCustomFromPromotionWorkflow = createWorkflow( "update-custom-from-promotion", (input: UpdateCustomFromPromotionStepInput) => { - const promotionData = useRemoteQueryStep({ - entry_point: "promotion", + const { data: promotions } = useQueryGraphStep({ + entity: "promotion", fields: ["custom.*"], - variables: { - filters: { - id: input.promotion.id - } - }, - list: false + filters: { + id: input.promotion.id, + } }) // TODO create, update, or delete Custom record @@ -558,9 +555,9 @@ Next, replace the `TODO` with the following: ```ts title="src/workflows/update-custom-from-promotion/index.ts" const created = when({ input, - promotionData + promotions }, (data) => - !data.promotionData.custom && + !data.promotions[0].custom && data.input.additional_data?.custom_name?.length > 0 ) .then(() => { @@ -592,25 +589,25 @@ Next, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-promotion/index.ts" const deleted = when({ input, - promotionData + promotions }, (data) => - data.promotionData.custom && ( + data.promotions[0].custom && ( data.input.additional_data?.custom_name === null || data.input.additional_data?.custom_name.length === 0 ) ) .then(() => { deleteCustomStep({ - custom: promotionData.custom + custom: promotions[0].custom }) dismissRemoteLinkStep({ [HELLO_MODULE]: { - custom_id: promotionData.custom.id + custom_id: promotions[0].custom.id } }) - return promotionData.custom.id + return promotions[0].custom.id }) // TODO delete Custom record @@ -623,11 +620,11 @@ Finally, replace the new `TODO` with the following: ```ts title="src/workflows/update-custom-from-promotion/index.ts" const updated = when({ input, - promotionData -}, (data) => data.promotionData.custom && data.input.additional_data?.custom_name?.length > 0) + promotions +}, (data) => data.promotions[0].custom && data.input.additional_data?.custom_name?.length > 0) .then(() => { const custom = updateCustomStep({ - id: promotionData.custom.id, + id: promotions[0].custom.id, custom_name: input.additional_data.custom_name }) diff --git a/www/apps/resources/app/deployment/medusa-application/railway/page.mdx b/www/apps/resources/app/deployment/medusa-application/railway/page.mdx index 744c29ec8b5e0..b587e81124274 100644 --- a/www/apps/resources/app/deployment/medusa-application/railway/page.mdx +++ b/www/apps/resources/app/deployment/medusa-application/railway/page.mdx @@ -2,7 +2,7 @@ sidebar_label: "Railway" --- -import { Prerequisites, DetailsList } from "docs-ui" +import { Prerequisites } from "docs-ui" export const metadata = { title: `Deploy Medusa Application to Railway`, @@ -82,6 +82,14 @@ Later, you’ll set different values of the `DISABLE_MEDUSA_ADMIN` environment v ### Configure Redis URL +The `redisUrl` configuration specifies the connection URL to Redis to store the Medusa server's session. + + + +Learn more in the [Medusa Configuration documentation](/references/medusa-config#redisurl). + + + Add the following configuration in `medusa-config.ts` : ```ts title="medusa-config.ts" diff --git a/www/apps/resources/app/deployment/storefront/vercel/page.mdx b/www/apps/resources/app/deployment/storefront/vercel/page.mdx index 790310b438532..43304af4f31b0 100644 --- a/www/apps/resources/app/deployment/storefront/vercel/page.mdx +++ b/www/apps/resources/app/deployment/storefront/vercel/page.mdx @@ -5,7 +5,7 @@ sidebar_label: "Vercel" import { Prerequisites } from "docs-ui" export const metadata = { - title: `Deploy Medusa Admin to Vercel`, + title: `Deploy Medusa Next.js to Vercel`, } # {metadata.title} diff --git a/www/apps/resources/app/medusa-cli/commands/build/page.mdx b/www/apps/resources/app/medusa-cli/commands/build/page.mdx index 14848f481d551..b82b60ad393e7 100644 --- a/www/apps/resources/app/medusa-cli/commands/build/page.mdx +++ b/www/apps/resources/app/medusa-cli/commands/build/page.mdx @@ -66,7 +66,13 @@ cd .medusa/server && npm install - When running the application locally, make sure to copy the `.env` file from the root project's directory. In production, use system environment variables instead. ```bash npm2yarn -cp .env .medusa/server +cp .env .medusa/server/.env.production +``` + +- In the system environment variables, set `NODE_ENV` to `production`: + +```bash +NODE_ENV=production ``` - Use the `start` command to run the application: diff --git a/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx b/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx index 9daf3d52c349a..920809d7587f9 100644 --- a/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx +++ b/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx @@ -1567,8 +1567,8 @@ To customize the cart completion flow, you’ll create a workflow and then use t ```mermaid graph TD - completeCartWorkflow["completeCartWorkflow (Medusa)"] --> useRemoteQueryStep["useRemoteQueryStep (Medusa)"] - useRemoteQueryStep --> when{order has digital products?} + completeCartWorkflow["completeCartWorkflow (Medusa)"] --> useQueryGraphStep["useQueryGraphStep (Medusa)"] + useQueryGraphStep --> when{order has digital products?} when -->|Yes| createDigitalProductOrderStep createDigitalProductOrderStep --> createRemoteLinkStep["createRemoteLinkStep (Medusa)"] createRemoteLinkStep --> createOrderFulfillmentWorkflow["createOrderFulfillmentWorkflow (Medusa)"] @@ -1580,7 +1580,7 @@ graph TD The workflow has the following steps: 1. `completeCartWorkflow` to create a Medusa order from the cart. Medusa provides this workflow through the `@medusajs/medusa/core-flows` package and you can use it as a step. -2. `useRemoteQueryStep` to retrieve the order’s items with the digital products associated with the purchased product variants. Medusa provides this step through the `@medusajs/medusa/core-flows` package. +2. `useQueryGraphStep` to retrieve the order’s items with the digital products associated with the purchased product variants. Medusa provides this step through the `@medusajs/medusa/core-flows` package. 3. If the order has digital products, you: 1. create the digital product order. 2. link the digital product order with the Medusa order. Medusa provides a `createRemoteLinkStep` in the `@medusajs/medusa/core-flows` package that can be used here. @@ -1665,14 +1665,14 @@ In the compensation function, you delete the digital product order. Create the file `src/workflows/create-digital-product-order/index.ts` with the following content: export const createDpoWorkflowHighlights = [ - ["25", "completeCartWorkflow", "Create an order for the cart."], - ["31", "useRemoteQueryStep", "Retrieve the order's items and their associated variants and linked digital products."], - ["56", "when", "Check whether the order has any digital products."], - ["61", "then", "Perform the callback function if an order has digital products."], - ["64", "createDigitalProductOrderStep", "Create the digital product order."], - ["66", "createRemoteLinkStep", "Link the digital product order to the Medusa order."], - ["75", "createOrderFulfillmentWorkflow", "Create a fulfillment for the digital products in the order."], - ["89", "emitEventStep", "Emit the `digital_product_order.created` event."] + ["27", "completeCartWorkflow", "Create an order for the cart."], + ["33", "useQueryGraphStep", "Retrieve the order's items and their associated variants and linked digital products."], + ["57", "when", "Check whether the order has any digital products."], + ["60", "then", "Perform the callback function if an order has digital products."], + ["63", "createDigitalProductOrderStep", "Create the digital product order."], + ["67", "createRemoteLinkStep", "Link the digital product order to the Medusa order."], + ["76", "createOrderFulfillmentWorkflow", "Create a fulfillment for the digital products in the order."], + ["90", "emitEventStep", "Emit the `digital_product_order.created` event."] ] ```ts title="src/workflows/create-digital-product-order/index.ts" highlights={createDpoWorkflowHighlights} collapsibleLines="1-17" expandMoreLabel="Show Imports" @@ -1680,16 +1680,18 @@ import { createWorkflow, transform, when, - WorkflowResponse, + WorkflowResponse } from "@medusajs/framework/workflows-sdk" import { completeCartWorkflow, - useRemoteQueryStep, + useQueryGraphStep, createRemoteLinkStep, createOrderFulfillmentWorkflow, - emitEventStep, + emitEventStep } from "@medusajs/medusa/core-flows" -import { Modules } from "@medusajs/framework/utils" +import { + Modules +} from "@medusajs/framework/utils" import createDigitalProductOrderStep from "./steps/create-digital-product-order" import { DIGITAL_PRODUCT_MODULE } from "../../modules/digital-product" @@ -1700,35 +1702,34 @@ type WorkflowInput = { const createDigitalProductOrderWorkflow = createWorkflow( "create-digital-product-order", (input: WorkflowInput) => { - const order = completeCartWorkflow.runAsStep({ + const { id } = completeCartWorkflow.runAsStep({ input: { - id: input.cart_id, - }, + id: input.cart_id + } }) - const { items } = useRemoteQueryStep({ - entry_point: "order", + const { data: orders } = useQueryGraphStep({ + entity: "order", fields: [ "*", "items.*", "items.variant.*", - "items.variant.digital_product.*", + "items.variant.digital_product.*" ], - variables: { - filters: { - id: order.id, - }, + filters: { + id }, - throw_if_key_not_found: true, - list: false, + options: { + throwIfKeyNotFound: true + } }) const itemsWithDigitalProducts = transform({ - items, + orders }, (data) => { - return data.items.filter((item) => item.variant.digital_product !== undefined) - } + return data.orders[0].items.filter((item) => item.variant.digital_product !== undefined) + } ) const digital_product_order = when(itemsWithDigitalProducts, (itemsWithDigitalProducts) => { @@ -1737,44 +1738,46 @@ const createDigitalProductOrderWorkflow = createWorkflow( .then(() => { const { digital_product_order, - } = createDigitalProductOrderStep({ items }) + } = createDigitalProductOrderStep({ + items: orders[0].items + }) createRemoteLinkStep([{ [DIGITAL_PRODUCT_MODULE]: { - digital_product_order_id: digital_product_order.id, + digital_product_order_id: digital_product_order.id }, [Modules.ORDER]: { - order_id: order.id, - }, + order_id: id + } }]) createOrderFulfillmentWorkflow.runAsStep({ input: { - order_id: order.id, + order_id: id, items: transform({ - itemsWithDigitalProducts, + itemsWithDigitalProducts }, (data) => { return data.itemsWithDigitalProducts.map((item) => ({ id: item.id, - quantity: item.quantity, + quantity: item.quantity })) - }), - }, + }) + } }) emitEventStep({ eventName: "digital_product_order.created", data: { - id: digital_product_order.id, - }, + id: digital_product_order.id + } }) return digital_product_order }) return new WorkflowResponse({ - order, - digital_product_order, + order: orders[0], + digital_product_order }) } ) @@ -1785,7 +1788,7 @@ export default createDigitalProductOrderWorkflow This creates the workflow `createDigitalProductOrderWorkflow`. It runs the following steps: 1. `completeCartWorkflow` as a step to create the Medusa order. -2. `useRemoteQueryStep` to retrieve the order’s items with their associated variants and linked digital products. +2. `useQueryGraphStep` to retrieve the order’s items with their associated variants and linked digital products. 3. Use `when` to check whether the order has digital products. If so: 1. Use the `createDigitalProductOrderStep` to create the digital product order. 2. Use the `createRemoteLinkStep` to link the digital product order to the Medusa order. @@ -1842,7 +1845,7 @@ In this step, you'll create a workflow that fulfills a digital order by sending The workflow has the following steps: -1. Retrieve the digital product order's details. For this, you'll use the `useRemoteQueryStep` imported from `@medusajs/medusa/core-flows`. +1. Retrieve the digital product order's details. For this, you'll use the `useQueryGraphStep` imported from `@medusajs/medusa/core-flows`. 2. Send a notification to the customer with the digital products to download. So, you only need to implement the second step. @@ -1959,17 +1962,17 @@ You use the `createNotifications` method of the Notification Module's main servi Create the workflow in the file `src/workflows/fulfill-digital-order/index.ts`: export const fulfillWorkflowHighlights = [ - ["17", "useRemoteQueryStep", "Retrieve the digital product order's details."], + ["17", "useQueryGraphStep", "Retrieve the digital product order's details."], ["33", "sendDigitalOrderNotificationStep", "Send a notification to the customer."] ] ```ts title="src/workflows/fulfill-digital-order/index.ts" highlights={fulfillWorkflowHighlights} collapsibleLines="1-10" expandMoreLabel="Show Imports" import { createWorkflow, - WorkflowResponse, + WorkflowResponse } from "@medusajs/framework/workflows-sdk" import { - useRemoteQueryStep, + useQueryGraphStep, } from "@medusajs/medusa/core-flows" import { sendDigitalOrderNotificationStep } from "./steps/send-digital-order-notification" @@ -1980,29 +1983,28 @@ type FulfillDigitalOrderWorkflowInput = { export const fulfillDigitalOrderWorkflow = createWorkflow( "fulfill-digital-order", ({ id }: FulfillDigitalOrderWorkflowInput) => { - const digitalProductOrder = useRemoteQueryStep({ - entry_point: "digital_product_order", + const { data: digitalProductOrders } = useQueryGraphStep({ + entity: "digital_product_order", fields: [ "*", "products.*", "products.medias.*", - "order.*", + "order.*" ], - variables: { - filters: { - id, - }, + filters: { + id, }, - list: false, - throw_if_key_not_found: true, + options: { + throwIfKeyNotFound: true + } }) sendDigitalOrderNotificationStep({ - digital_product_order: digitalProductOrder, + digital_product_order: digitalProductOrders[0] }) return new WorkflowResponse( - digitalProductOrder + digitalProductOrders[0] ) } ) @@ -2010,7 +2012,7 @@ export const fulfillDigitalOrderWorkflow = createWorkflow( In the workflow, you: -1. Retrieve the digital product order's details using the `useRemoteQueryStep` imported from `@medusajs/medusa/core-flows`. +1. Retrieve the digital product order's details using the `useQueryGraphStep` imported from `@medusajs/medusa/core-flows`. 2. Send a notification to the customer with the digital product download links using the `sendDigitalOrderNotificationStep`. ### Configure Notification Module Provider diff --git a/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx b/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx index 4964ce3332477..b9c952c4145d3 100644 --- a/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx +++ b/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx @@ -1119,7 +1119,7 @@ import { } from "@medusajs/framework/workflows-sdk" import { setAuthAppMetadataStep, - useRemoteQueryStep, + useQueryGraphStep, } from "@medusajs/medusa/core-flows" import { deleteRestaurantAdminStep } from "../steps/delete-restaurant-admin" @@ -1144,14 +1144,12 @@ So far, you only use the `deleteRestaurantAdminStep` in the workflow, which dele Replace the `TODO` with the following: ```ts title="restaurant-marketplace/src/workflows/restaurant/workflows/delete-restaurant-admin.ts" -const authIdentities = useRemoteQueryStep({ - entry_point: "auth_identity", +const { data: authIdentities } = useQueryGraphStep({ + entity: "auth_identity", fields: ["id"], - variables: { - filters: { - app_metadata: { - restaurant_id: input.id, - }, + filters: { + app_metadata: { + restaurant_id: input.id, }, }, }) diff --git a/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx b/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx index 748bfc935afa8..26d6b0e158764 100644 --- a/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx +++ b/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx @@ -803,14 +803,14 @@ In this step, you’ll create a workflow that’s executed when the customer pla ```mermaid graph TD - retrieveCartStep["Retrieve Cart (useRemoteQueryStep from Medusa)"] --> completeCartWorkflow["completeCartWorkflow (Medusa)"] + retrieveCartStep["Retrieve Cart (useQueryGraphStep from Medusa)"] --> completeCartWorkflow["completeCartWorkflow (Medusa)"] completeCartWorkflow["completeCartWorkflow (Medusa)"] --> groupVendorItemsStep groupVendorItemsStep --> getOrderDetailWorkflow getOrderDetailWorkflow --> createVendorOrdersStep createVendorOrdersStep --> createRemoteLinkStep["Create Links (createRemoteLinkStep from Medusa)"] ``` -1. Retrieve the cart using its ID. Medusa provides a `useRemoteQueryStep` in the `@medusajs/medusa/core-flows` package that you can use. +1. Retrieve the cart using its ID. Medusa provides a `useQueryGraphStep` in the `@medusajs/medusa/core-flows` package that you can use. 2. Create a parent order for the cart and its items. Medusa also has a `completeCartWorkflow` in the `@medusajs/medusa/core-flows` package that you can use as a step. 3. Group the cart items by their product’s associated vendor. 4. Retrieve the order's details using Medusa's `getOrderDetailWorkflow` exported by the `@medusajs/medusa/core-flows` package. @@ -1135,25 +1135,24 @@ The compensation function cancels all child orders received from the step. It us Finally, create the workflow at the file `src/workflows/marketplace/create-vendor-orders/index.ts`: export const createVendorOrdersWorkflowHighlights = [ - ["21", "useRemoteQueryStep", "Retrieve the cart's details."], - ["29", "completeCartWorkflow", "Create the parent order from the cart."], - ["35", "groupVendorItemsStep", "Group the items by their vendor."], - ["42", "createVendorOrdersStep", "Create child orders for each vendor"], - ["47", "createRemoteLinkStep", "Create the links returned by the previous step."] + ["21", "useQueryGraphStep", "Retrieve the cart's details."], + ["30", "completeCartWorkflow", "Create the parent order from the cart."], + ["36", "groupVendorItemsStep", "Group the items by their vendor."], + ["59", "createVendorOrdersStep", "Create child orders for each vendor"], + ["64", "createRemoteLinkStep", "Create the links returned by the previous step."] ] ```ts title="src/workflows/marketplace/create-vendor-orders/index.ts" collapsibleLines="1-13" expandMoreLabel="Show Imports" import { createWorkflow, - WorkflowResponse, + WorkflowResponse } from "@medusajs/framework/workflows-sdk" import { - useRemoteQueryStep, + useQueryGraphStep, createRemoteLinkStep, completeCartWorkflow, getOrderDetailWorkflow } from "@medusajs/medusa/core-flows" -import { CartDTO } from "@medusajs/framework/types" import groupVendorItemsStep from "./steps/group-vendor-items" import createVendorOrdersStep from "./steps/create-vendor-orders" @@ -1164,24 +1163,25 @@ type WorkflowInput = { const createVendorOrdersWorkflow = createWorkflow( "create-vendor-order", (input: WorkflowInput) => { - const cart = useRemoteQueryStep({ - entry_point: "cart", - fields: ["items.*"], - variables: { id: input.cart_id }, - list: false, - throw_if_key_not_found: true, - }) as CartDTO + const { data: carts } = useQueryGraphStep({ + entity: "cart", + fields: ['items.*'], + filters: { id: input.cart_id }, + options: { + throwIfKeyNotFound: true + } + }) const { id: orderId } = completeCartWorkflow.runAsStep({ input: { - id: cart.id + id: carts[0].id } }) const { vendorsItems } = groupVendorItemsStep({ - cart, + cart: carts[0].id }) - + const order = getOrderDetailWorkflow.runAsStep({ input: { order_id: orderId, @@ -1200,17 +1200,17 @@ const createVendorOrdersWorkflow = createWorkflow( const { orders: vendorOrders, - linkDefs, + linkDefs } = createVendorOrdersStep({ parentOrder: order, - vendorsItems, + vendorsItems }) createRemoteLinkStep(linkDefs) return new WorkflowResponse({ parent_order: order, - vendor_orders: vendorOrders, + vendor_orders: vendorOrders }) } ) @@ -1220,11 +1220,12 @@ export default createVendorOrdersWorkflow In the workflow, you run the following steps: -1. `useRemoteQueryStep` to retrieve the cart's details. +1. `useQueryGraphStep` to retrieve the cart's details. 2. `completeCartWorkflow` to complete the cart and create a parent order. 3. `groupVendorItemsStep` to group the order's items by their vendor. -4. `createVendorOrdersStep` to create child orders for each vendor's items. -5. `createRemoteLinkStep` to create the links returned by the previous step. +4. `getOrderDetailWorkflow` to retrieve an order's details. +5. `createVendorOrdersStep` to create child orders for each vendor's items. +6. `createRemoteLinkStep` to create the links returned by the previous step. You return the parent and vendor orders. diff --git a/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx b/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx index 84f42e854346b..f3a35c6a9b066 100644 --- a/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx +++ b/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx @@ -592,23 +592,23 @@ Create the file `src/workflows/create-subscription/index.ts` with the following export const createSubscriptionWorkflowHighlights = [ ["26", "completeCartWorkflow", "Complete the cart and create the order."], - ["32", "useRemoteQueryStep", "Retrieve the order's details."], - ["44", "createSubscriptionStep", "Create the subscription."], - ["51", "createRemoteLinkStep", "Create the links returned by the previous step."] + ["32", "useQueryGraphStep", "Retrieve the order's details."], + ["43", "createSubscriptionStep", "Create the subscription."], + ["50", "createRemoteLinkStep", "Create the links returned by the previous step."] ] ```ts title="src/workflows/create-subscription/index.ts" highlights={createSubscriptionWorkflowHighlights} collapsibleLines="1-13" expandMoreLabel="Show Imports" import { createWorkflow, - WorkflowResponse, + WorkflowResponse } from "@medusajs/framework/workflows-sdk" import { createRemoteLinkStep, completeCartWorkflow, - useRemoteQueryStep + useQueryGraphStep } from "@medusajs/medusa/core-flows" import { - SubscriptionInterval, + SubscriptionInterval } from "../../modules/subscription/types" import createSubscriptionStep from "./steps/create-subscription" @@ -629,30 +629,29 @@ const createSubscriptionWorkflow = createWorkflow( } }) - const order = useRemoteQueryStep({ - entry_point: "order", + const { data: orders } = useQueryGraphStep({ + entity: "order", fields: ["*", "id", "customer_id"], - variables: { - filters: { - id - } + filters: { + id }, - list: false, - throw_if_key_not_found: true + options: { + throwIfKeyNotFound: true + } }) const { subscription, linkDefs } = createSubscriptionStep({ cart_id: input.cart_id, - order_id: order.id, - customer_id: order.customer_id, - subscription_data: input.subscription_data, + order_id: orders[0].id, + customer_id: orders[0].customer_id, + subscription_data: input.subscription_data }) createRemoteLinkStep(linkDefs) return new WorkflowResponse({ subscription: subscription, - order: order, + order: orders[0] }) } ) @@ -663,7 +662,7 @@ export default createSubscriptionWorkflow This workflow accepts the cart’s ID, along with the subscription details. It executes the following steps: 1. `completeCartWorkflow` from `@medusajs/medusa/core-flows` that completes a cart and creates an order. -2. `useRemoteQueryStep` from `@medusajs/medusa/core-flows` to retrieve the order's details. +2. `useQueryGraphStep` from `@medusajs/medusa/core-flows` to retrieve the order's details. 3. `createSubscriptionStep`, which is the step you created previously. 4. `createRemoteLinkStep` from `@medusajs/medusa/core-flows`, which accepts links to create. These links are in the `linkDefs` array returned by the previous step. @@ -1478,7 +1477,7 @@ The workflow has eight steps: ```mermaid graph TD - useRemoteQueryStep["Retrieve Cart (useRemoteQueryStep by Medusa)"] --> createPaymentCollectionStep["createPaymentCollectionStep (Medusa)"] + useQueryGraphStep["Retrieve Cart (useQueryGraphStep by Medusa)"] --> createPaymentCollectionStep["createPaymentCollectionStep (Medusa)"] createPaymentCollectionStep["createPaymentCollectionStep (Medusa)"] --> createPaymentSessionsWorkflow["createPaymentSessionsWorkflow (Medusa)"] createPaymentSessionsWorkflow["createPaymentSessionsWorkflow (Medusa)"] --> authorizePaymentSessionStep["authorizePaymentSessionStep (Medusa)"] authorizePaymentSessionStep["authorizePaymentSessionStep (Medusa)"] --> createSubscriptionOrderStep @@ -1487,7 +1486,7 @@ graph TD capturePaymentStep["capturePaymentStep (Medusa)"] --> updateSubscriptionStep ``` -1. Retrieve the subscription’s linked cart. Medusa provides a `useRemoteQueryStep` in the `@medusajs/medusa/core-flows` package that can be used as a step. +1. Retrieve the subscription’s linked cart. Medusa provides a `useQueryGraphStep` in the `@medusajs/medusa/core-flows` package that can be used as a step. 2. Create a payment collection for the new order. Medusa provides a `createPaymentCollectionsStep` in the `@medusajs/medusa/core-flows` package that you can use. 3. Create payment sessions in the payment collection. Medusa provides a `createPaymentSessionsWorkflow` in the `@medusajs/medusa/core-flows` package that can be used as a step. 4. Authorize the payment session. Medusa also provides the `authorizePaymentSessionStep` in the `@medusajs/medusa/core-flows` package, which can be used. @@ -1771,38 +1770,30 @@ This updates the subscription’s `last_order_date` and `next_order_date` proper Finally, create the file `src/workflows/create-subscription-order/index.ts` with the following content: export const createSubscriptionOrderWorkflowHighlights = [ - ["33", "useRemoteQueryStep", "Retrieve the cart linked to the subscription."], - ["60", "createPaymentCollectionsStep", "Create a payment collection using the same information in the cart."], - ["67", "createPaymentSessionsWorkflow", "Create a payment session in the payment collection from the previous step."], - ["76", "authorizePaymentSessionStep", "Authorize the payment session created from the first step."], - ["81", "createSubscriptionOrderStep", "Create the new order for the subscription."], - ["87", "createRemoteLinkStep", "Create links returned by the previous step."], - ["89", "capturePaymentStep", "Capture the order’s payment."], - ["94", "updateSubscriptionStep", "Update the subscription’s `last_order_date` and `next_order_date`."] + ["25", "useQueryGraphStep", "Retrieve the cart linked to the subscription."], + ["49", "createPaymentCollectionsStep", "Create a payment collection using the same information in the cart."], + ["56", "createPaymentSessionsWorkflow", "Create a payment session in the payment collection from the previous step."], + ["65", "authorizePaymentSessionStep", "Authorize the payment session created from the first step."], + ["70", "createSubscriptionOrderStep", "Create the new order for the subscription."], + ["76", "createRemoteLinkStep", "Create links returned by the previous step."], + ["78", "capturePaymentStep", "Capture the order’s payment."], + ["83", "updateSubscriptionStep", "Update the subscription’s `last_order_date` and `next_order_date`."] ] ```ts title="src/workflows/create-subscription-order/index.ts" highlights={createSubscriptionOrderWorkflowHighlights} collapsibleLines="1-25" expandMoreLabel="Show Imports" +import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk" import { - createWorkflow, - WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" -import { - useRemoteQueryStep, + useQueryGraphStep, createPaymentSessionsWorkflow, createRemoteLinkStep, - capturePaymentStep, + capturePaymentStep } from "@medusajs/medusa/core-flows" import { - CartWorkflowDTO, -} from "@medusajs/framework/types" -import { - SubscriptionData, + SubscriptionData } from "../../modules/subscription/types" import { authorizePaymentSessionStep, -} from "@medusajs/medusa/core-flows" -import { - createPaymentCollectionsStep, + createPaymentCollectionsStep } from "@medusajs/medusa/core-flows" import createSubscriptionOrderStep from "./steps/create-subscription-order" import updateSubscriptionStep from "./steps/update-subscription" @@ -1814,8 +1805,8 @@ type WorkflowInput = { const createSubscriptionOrderWorkflow = createWorkflow( "create-subscription-order", (input: WorkflowInput) => { - const { cart } = useRemoteQueryStep({ - entry_point: "subscription", + const { data: carts } = useQueryGraphStep({ + entity: "subscription", fields: [ "*", "cart.*", @@ -1828,59 +1819,56 @@ const createSubscriptionOrderWorkflow = createWorkflow( "cart.shipping_methods.tax_lines.*", "cart.shipping_methods.adjustments.*", "cart.payment_collection.*", - "cart.payment_collection.payment_sessions.*", + "cart.payment_collection.payment_sessions.*" ], - variables: { - filters: { - id: [input.subscription.id], - }, + filters: { + id: [input.subscription.id] }, - list: false, - throw_if_key_not_found: true, - }) as { - cart: CartWorkflowDTO - } + options: { + throwIfKeyNotFound: true + } + }) const payment_collection = createPaymentCollectionsStep([{ - region_id: cart.region_id, - currency_code: cart.currency_code, - amount: cart.payment_collection.amount, - metadata: cart.payment_collection.metadata, + region_id: carts[0].region_id, + currency_code: carts[0].currency_code, + amount: carts[0].payment_collection.amount, + metadata: carts[0].payment_collection.metadata }])[0] const paymentSession = createPaymentSessionsWorkflow.runAsStep({ input: { payment_collection_id: payment_collection.id, - provider_id: cart.payment_collection.payment_sessions[0].provider_id, - data: cart.payment_collection.payment_sessions[0].data, - context: cart.payment_collection.payment_sessions[0].context, - }, + provider_id: carts[0].payment_collection.payment_sessions[0].provider_id, + data: carts[0].payment_collection.payment_sessions[0].data, + context: carts[0].payment_collection.payment_sessions[0].context + } }) const payment = authorizePaymentSessionStep({ id: paymentSession.id, - context: paymentSession.context, + context: paymentSession.context }) const { order, linkDefs } = createSubscriptionOrderStep({ subscription: input.subscription, - cart, - payment_collection, + cart: carts[0], + payment_collection }) createRemoteLinkStep(linkDefs) capturePaymentStep({ payment_id: payment.id, - amount: payment.amount, + amount: payment.amount }) updateSubscriptionStep({ - subscription_id: input.subscription.id, + subscription_id: input.subscription.id }) return new WorkflowResponse({ - order, + order }) } ) @@ -1890,7 +1878,7 @@ export default createSubscriptionOrderWorkflow The workflow runs the following steps: -1. `useRemoteQueryStep` to retrieve the details of the cart linked to the subscription. +1. `useQueryGraphStep` to retrieve the details of the cart linked to the subscription. 2. `createPaymentCollectionsStep` to create a payment collection using the same information in the cart. 3. `createPaymentSessionsWorkflow` to create a payment session in the payment collection from the previous step. 4. `authorizePaymentSessionStep` to authorize the payment session created from the first step. diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs index 1b90ee3bf3e8e..f87fcc1468389 100644 --- a/www/apps/resources/generated/edit-dates.mjs +++ b/www/apps/resources/generated/edit-dates.mjs @@ -117,7 +117,7 @@ export const generatedEditDates = { "app/contribution-guidelines/docs/page.mdx": "2024-10-16T15:48:04.071Z", "app/create-medusa-app/page.mdx": "2024-08-05T11:10:55+03:00", "app/deployment/admin/vercel/page.mdx": "2024-10-16T08:10:29.377Z", - "app/deployment/medusa-application/railway/page.mdx": "2024-10-22T11:02:09.029Z", + "app/deployment/medusa-application/railway/page.mdx": "2024-11-11T11:50:10.517Z", "app/deployment/storefront/vercel/page.mdx": "2024-07-26T07:21:31+00:00", "app/deployment/page.mdx": "2024-07-25T09:55:22+03:00", "app/integrations/page.mdx": "2024-10-15T12:26:39.839Z", @@ -236,7 +236,7 @@ export const generatedEditDates = { "app/architectural-modules/cache/create/page.mdx": "2024-10-16T08:51:35.074Z", "app/admin-widget-injection-zones/page.mdx": "2024-09-30T08:43:53.147Z", "app/architectural-modules/notification/page.mdx": "2024-10-15T12:51:28.735Z", - "app/architectural-modules/event/create/page.mdx": "2024-10-16T08:51:41.334Z", + "app/architectural-modules/event/create/page.mdx": "2024-11-12T11:54:51.583Z", "references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:58.913Z", "references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityWorkflow/page.mdx": "2024-08-20T00:10:58.949Z", "references/core_flows/Order/functions/core_flows.Order.updateOrderEditItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:59.121Z", @@ -447,9 +447,9 @@ export const generatedEditDates = { "references/fulfillment_models/classes/fulfillment_models.ShippingOptionRule/page.mdx": "2024-10-03T00:12:15.551Z", "references/fulfillment_models/classes/fulfillment_models.ShippingOptionType/page.mdx": "2024-10-03T00:12:15.557Z", "references/fulfillment_models/classes/fulfillment_models.ShippingProfile/page.mdx": "2024-10-03T00:12:15.581Z", - "references/helper_steps/functions/helper_steps.createRemoteLinkStep/page.mdx": "2024-10-03T00:12:16.458Z", - "references/helper_steps/functions/helper_steps.dismissRemoteLinkStep/page.mdx": "2024-10-03T00:12:16.461Z", - "references/helper_steps/functions/helper_steps.emitEventStep/page.mdx": "2024-10-03T00:12:16.464Z", + "references/helper_steps/functions/helper_steps.createRemoteLinkStep/page.mdx": "2024-11-12T16:19:00.137Z", + "references/helper_steps/functions/helper_steps.dismissRemoteLinkStep/page.mdx": "2024-11-12T16:19:00.139Z", + "references/helper_steps/functions/helper_steps.emitEventStep/page.mdx": "2024-11-12T16:19:00.140Z", "references/helper_steps/functions/helper_steps.updateRemoteLinksStep/page.mdx": "2024-10-02T00:12:16.726Z", "references/helper_steps/types/helper_steps.DismissRemoteLinksStepInput/page.mdx": "2024-08-28T00:11:31.042Z", "references/medusa_config/interfaces/medusa_config.AdminOptions/page.mdx": "2024-11-06T21:09:09.666Z", @@ -737,7 +737,7 @@ export const generatedEditDates = { "references/core_flows/types/core_flows.OrderEditRequestWorkflowInput/page.mdx": "2024-10-23T07:15:51.428Z", "references/core_flows/types/core_flows.UpdateOrderTaxLinesWorkflowInput/page.mdx": "2024-10-23T07:15:51.436Z", "references/core_flows/types/core_flows.UpdateTaxLinesWorkflowInput/page.mdx": "2024-11-06T21:09:04.554Z", - "references/helper_steps/functions/helper_steps.useRemoteQueryStep/page.mdx": "2024-10-03T00:12:16.474Z", + "references/helper_steps/functions/helper_steps.useRemoteQueryStep/page.mdx": "2024-11-12T16:19:00.148Z", "references/modules/types/page.mdx": "2024-11-12T09:36:21.248Z", "references/order/IOrderModuleService/methods/order.IOrderModuleService.cancelReturn/page.mdx": "2024-11-06T21:09:11.354Z", "references/order/interfaces/order.CancelOrderClaimDTO/page.mdx": "2024-10-03T00:12:17.706Z", @@ -1776,7 +1776,7 @@ export const generatedEditDates = { "references/dml/Property_Types/methods/dml.Property_Types.json/page.mdx": "2024-10-03T00:12:15.464Z", "references/dml/Property_Types/methods/dml.Property_Types.number/page.mdx": "2024-10-03T00:12:15.459Z", "references/dml/Property_Types/methods/dml.Property_Types.text/page.mdx": "2024-10-03T00:12:15.457Z", - "references/helper_steps/functions/helper_steps.removeRemoteLinkStep/page.mdx": "2024-10-03T00:12:16.466Z", + "references/helper_steps/functions/helper_steps.removeRemoteLinkStep/page.mdx": "2024-11-12T16:19:00.142Z", "references/modules/workflows/page.mdx": "2024-09-30T08:43:53.315Z", "references/search/classes/search.AbstractSearchService/page.mdx": "2024-10-14T09:11:45.913Z", "references/workflows/StepResponse/methods/workflows.StepResponse.permanentFailure/page.mdx": "2024-10-02T00:12:20.683Z", @@ -2276,7 +2276,7 @@ export const generatedEditDates = { "app/commerce-modules/stock-location/links-to-other-modules/page.mdx": "2024-10-15T14:33:11.483Z", "app/commerce-modules/store/links-to-other-modules/page.mdx": "2024-06-26T07:19:49.931Z", "app/examples/page.mdx": "2024-10-16T15:47:38.345Z", - "app/medusa-cli/commands/build/page.mdx": "2024-10-16T08:16:27.618Z", + "app/medusa-cli/commands/build/page.mdx": "2024-11-11T11:00:49.665Z", "app/js-sdk/page.mdx": "2024-10-16T12:12:34.512Z", "references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.apiKey/page.mdx": "2024-10-25T15:35:28.397Z", "references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.campaign/page.mdx": "2024-10-25T15:35:28.465Z", @@ -2716,17 +2716,17 @@ export const generatedEditDates = { "references/js_sdk/auth/Auth/methods/js_sdk.auth.Auth.updateProvider/page.mdx": "2024-10-24T13:48:31.024Z", "references/js_sdk/auth/classes/js_sdk.auth.Auth/page.mdx": "2024-10-22T15:09:53.780Z", "references/js_sdk/modules/js_sdk.admin/page.mdx": "2024-10-22T15:09:52.264Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx": "2024-11-11T10:32:09.787Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx": "2024-11-12T16:19:01.705Z", "references/js_sdk/modules/js_sdk.store/page.mdx": "2024-10-22T15:09:53.787Z", "references/js_sdk/modules/js_sdk.auth/page.mdx": "2024-10-22T15:09:53.779Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx": "2024-10-25T15:35:30.345Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx": "2024-11-11T10:32:09.851Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx": "2024-11-11T10:32:09.935Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx": "2024-10-25T15:35:30.425Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx": "2024-11-11T10:32:09.891Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx": "2024-11-11T10:32:09.875Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx": "2024-11-11T10:32:09.803Z", - "references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx": "2024-11-11T10:32:09.763Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx": "2024-11-12T16:19:01.699Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx": "2024-11-12T16:19:01.735Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx": "2024-11-12T16:19:01.773Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx": "2024-11-12T16:19:01.739Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx": "2024-11-12T16:19:01.753Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx": "2024-11-12T16:19:01.748Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx": "2024-11-12T16:19:01.712Z", + "references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx": "2024-11-12T16:19:01.693Z", "references/js_sdk/store/classes/js_sdk.store.Store/page.mdx": "2024-10-22T15:09:53.788Z", "references/modules/js_sdk/page.mdx": "2024-10-22T15:09:52.263Z", "references/core_flows/Inventory/Steps_Inventory/functions/core_flows.Inventory.Steps_Inventory.validateInventoryDeleteStep/page.mdx": "2024-10-23T07:15:37.028Z", diff --git a/www/apps/resources/generated/sidebar.mjs b/www/apps/resources/generated/sidebar.mjs index 14b1f29357705..39f95c46ad034 100644 --- a/www/apps/resources/generated/sidebar.mjs +++ b/www/apps/resources/generated/sidebar.mjs @@ -8616,7 +8616,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/cart", + "path": "/references/js-sdk/store/cart", "title": "cart", "children": [] }, @@ -8624,7 +8624,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/category", + "path": "/references/js-sdk/store/category", "title": "category", "children": [] }, @@ -8632,7 +8632,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/collection", + "path": "/references/js-sdk/store/collection", "title": "collection", "children": [] }, @@ -8640,7 +8640,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/customer", + "path": "/references/js-sdk/store/customer", "title": "customer", "children": [] }, @@ -8648,7 +8648,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/fulfillment", + "path": "/references/js-sdk/store/fulfillment", "title": "fulfillment", "children": [] }, @@ -8656,7 +8656,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/order", + "path": "/references/js-sdk/store/order", "title": "order", "children": [] }, @@ -8664,7 +8664,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/payment", + "path": "/references/js-sdk/store/payment", "title": "payment", "children": [] }, @@ -8672,7 +8672,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/product", + "path": "/references/js-sdk/store/product", "title": "product", "children": [] }, @@ -8680,7 +8680,7 @@ export const generatedSidebar = [ "loaded": true, "isPathHref": true, "type": "link", - "path": "/references/js-sdk/admin/region", + "path": "/references/js-sdk/store/region", "title": "region", "children": [] } @@ -9066,8 +9066,15 @@ export const generatedSidebar = [ "isPathHref": true, "type": "category", "title": "Medusa Application", - "autogenerate_path": "/deployment/medusa-application", "children": [ + { + "loaded": true, + "isPathHref": true, + "type": "link", + "path": "https://docs.medusajs.com/learn/deployment/general", + "title": "General", + "children": [] + }, { "loaded": true, "isPathHref": true, diff --git a/www/apps/resources/generated/slug-changes.mjs b/www/apps/resources/generated/slug-changes.mjs index 8bd3e8cd1e77f..8aa276b041602 100644 --- a/www/apps/resources/generated/slug-changes.mjs +++ b/www/apps/resources/generated/slug-changes.mjs @@ -4336,47 +4336,47 @@ export const slugChanges = [ }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.cart", - "newSlug": "/references/js-sdk/admin/cart", + "newSlug": "/references/js-sdk/store/cart", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.category", - "newSlug": "/references/js-sdk/admin/category", + "newSlug": "/references/js-sdk/store/category", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.collection", - "newSlug": "/references/js-sdk/admin/collection", + "newSlug": "/references/js-sdk/store/collection", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.customer", - "newSlug": "/references/js-sdk/admin/customer", + "newSlug": "/references/js-sdk/store/customer", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment", - "newSlug": "/references/js-sdk/admin/fulfillment", + "newSlug": "/references/js-sdk/store/fulfillment", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.order", - "newSlug": "/references/js-sdk/admin/order", + "newSlug": "/references/js-sdk/store/order", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.payment", - "newSlug": "/references/js-sdk/admin/payment", + "newSlug": "/references/js-sdk/store/payment", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.product", - "newSlug": "/references/js-sdk/admin/product", + "newSlug": "/references/js-sdk/store/product", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx" }, { "origSlug": "/references/js_sdk/store/Store/properties/js_sdk.store.Store.region", - "newSlug": "/references/js-sdk/admin/region", + "newSlug": "/references/js-sdk/store/region", "filePath": "/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx" }, { diff --git a/www/apps/resources/references/helper_steps/functions/helper_steps.createRemoteLinkStep/page.mdx b/www/apps/resources/references/helper_steps/functions/helper_steps.createRemoteLinkStep/page.mdx index 465f9c0334f05..5560f0ca1237c 100644 --- a/www/apps/resources/references/helper_steps/functions/helper_steps.createRemoteLinkStep/page.mdx +++ b/www/apps/resources/references/helper_steps/functions/helper_steps.createRemoteLinkStep/page.mdx @@ -9,6 +9,10 @@ import { TypeList } from "docs-ui" This documentation provides a reference to the `createRemoteLinkStep` step. It belongs to the `@medusajs/medusa/core-flows` package. +This step creates remote links between two records of linked data models. + +Learn more in the [Remote Link documentation.](https://docs.medusajs.com/advanced-development/modules/remote-link#create-link). + ## Example ```ts diff --git a/www/apps/resources/references/helper_steps/functions/helper_steps.dismissRemoteLinkStep/page.mdx b/www/apps/resources/references/helper_steps/functions/helper_steps.dismissRemoteLinkStep/page.mdx index 6203d92aa1dfa..15cd626a6bc15 100644 --- a/www/apps/resources/references/helper_steps/functions/helper_steps.dismissRemoteLinkStep/page.mdx +++ b/www/apps/resources/references/helper_steps/functions/helper_steps.dismissRemoteLinkStep/page.mdx @@ -9,6 +9,10 @@ import { TypeList } from "docs-ui" This documentation provides a reference to the `dismissRemoteLinkStep` step. It belongs to the `@medusajs/medusa/core-flows` package. +This step removes remote links between two records of linked data models. + +Learn more in the [Remote Link documentation.](https://docs.medusajs.com/advanced-development/modules/remote-link#dismiss-link). + ## Example ```ts diff --git a/www/apps/resources/references/helper_steps/functions/helper_steps.emitEventStep/page.mdx b/www/apps/resources/references/helper_steps/functions/helper_steps.emitEventStep/page.mdx index 9c0b0b6f41387..b775b8710acab 100644 --- a/www/apps/resources/references/helper_steps/functions/helper_steps.emitEventStep/page.mdx +++ b/www/apps/resources/references/helper_steps/functions/helper_steps.emitEventStep/page.mdx @@ -9,6 +9,8 @@ import { TypeList } from "docs-ui" This documentation provides a reference to the `emitEventStep` step. It belongs to the `@medusajs/medusa/core-flows` package. +Emit an event. + ## Example ```ts diff --git a/www/apps/resources/references/helper_steps/functions/helper_steps.removeRemoteLinkStep/page.mdx b/www/apps/resources/references/helper_steps/functions/helper_steps.removeRemoteLinkStep/page.mdx index 4d1322ccb6d49..b385a7a745598 100644 --- a/www/apps/resources/references/helper_steps/functions/helper_steps.removeRemoteLinkStep/page.mdx +++ b/www/apps/resources/references/helper_steps/functions/helper_steps.removeRemoteLinkStep/page.mdx @@ -9,6 +9,10 @@ import { TypeList } from "docs-ui" This documentation provides a reference to the `removeRemoteLinkStep` step. It belongs to the `@medusajs/medusa/core-flows` package. +This step deletes linked records of a record. + +Learn more in the [Remote Link documentation](https://docs.medusajs.com/advanced-development/modules/remote-link#cascade-delete-linked-records) + ## Example ```ts diff --git a/www/apps/resources/references/helper_steps/functions/helper_steps.useRemoteQueryStep/page.mdx b/www/apps/resources/references/helper_steps/functions/helper_steps.useRemoteQueryStep/page.mdx index af4277af7aae0..e540a8fbd0abb 100644 --- a/www/apps/resources/references/helper_steps/functions/helper_steps.useRemoteQueryStep/page.mdx +++ b/www/apps/resources/references/helper_steps/functions/helper_steps.useRemoteQueryStep/page.mdx @@ -9,6 +9,10 @@ import { TypeList } from "docs-ui" This documentation provides a reference to the `useRemoteQueryStep` step. It belongs to the `@medusajs/medusa/core-flows` package. +This step fetches data across modules using the remote query. + +Learn more in the [Remote Query documentation](https://docs.medusajs.com/advanced-development/modules/remote-query). + ## Example To retrieve a list of records of a data model: diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx index 6b29b7b90a750..1880edf2df14f 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.cart/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/cart +slug: /references/js-sdk/store/cart sidebar_label: cart --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx index 6dc2b9c991783..d9352bb8227c4 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.category/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/category +slug: /references/js-sdk/store/category sidebar_label: category --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx index 0019fcc3a8af4..330404669f22c 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.collection/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/collection +slug: /references/js-sdk/store/collection sidebar_label: collection --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx index 1940cc0a0d02f..3f0cceac12413 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.customer/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/customer +slug: /references/js-sdk/store/customer sidebar_label: customer --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx index 8942eed630d18..a88a99d0cab5c 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.fulfillment/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/fulfillment +slug: /references/js-sdk/store/fulfillment sidebar_label: fulfillment --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx index ab006d5407b45..da592e42e4a40 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.order/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/order +slug: /references/js-sdk/store/order sidebar_label: order --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx index d8afe90fc23a4..1ec13b86a9cd0 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.payment/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/payment +slug: /references/js-sdk/store/payment sidebar_label: payment --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx index d5ab9232eeb0e..7e40f55b59e1f 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.product/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/product +slug: /references/js-sdk/store/product sidebar_label: product --- diff --git a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx index 3d3e1c0ac7f5d..843ff83b57c09 100644 --- a/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx +++ b/www/apps/resources/references/js_sdk/store/Store/properties/js_sdk.store.Store.region/page.mdx @@ -1,5 +1,5 @@ --- -slug: /references/js-sdk/admin/region +slug: /references/js-sdk/store/region sidebar_label: region --- diff --git a/www/apps/resources/sidebar.mjs b/www/apps/resources/sidebar.mjs index 6b0fdac0e4385..f5b8ade77214d 100644 --- a/www/apps/resources/sidebar.mjs +++ b/www/apps/resources/sidebar.mjs @@ -2100,7 +2100,18 @@ export const sidebar = sidebarAttachHrefCommonOptions([ { type: "category", title: "Medusa Application", - autogenerate_path: "/deployment/medusa-application", + children: [ + { + type: "link", + path: "https://docs.medusajs.com/learn/deployment/general", + title: "General" + }, + { + type: "link", + path: "/deployment/medusa-application/railway", + title: "Railway" + }, + ], }, { type: "category", diff --git a/www/packages/docs-ui/src/components/CodeBlock/Header/index.tsx b/www/packages/docs-ui/src/components/CodeBlock/Header/index.tsx index b6be62c9d8511..1121c46c1e8a7 100644 --- a/www/packages/docs-ui/src/components/CodeBlock/Header/index.tsx +++ b/www/packages/docs-ui/src/components/CodeBlock/Header/index.tsx @@ -17,6 +17,7 @@ type CodeBlockHeaderProps = { title?: string blockStyle?: CodeBlockStyle actionsProps: CodeBlockActionsProps + hideActions?: boolean } & CodeBlockHeaderMeta export const CodeBlockHeader = ({ @@ -25,6 +26,7 @@ export const CodeBlockHeader = ({ badgeLabel, actionsProps, badgeColor, + hideActions = false }: CodeBlockHeaderProps) => { const { colorMode } = useColorMode() @@ -54,7 +56,7 @@ export const CodeBlockHeader = ({ )} - + {!hideActions && } ) } diff --git a/www/packages/docs-ui/src/components/CodeBlock/index.tsx b/www/packages/docs-ui/src/components/CodeBlock/index.tsx index 8d8befeab7698..eefce95c1d914 100644 --- a/www/packages/docs-ui/src/components/CodeBlock/index.tsx +++ b/www/packages/docs-ui/src/components/CodeBlock/index.tsx @@ -363,6 +363,7 @@ export const CodeBlock = ({ ...actionsProps, inHeader: true, }} + hideActions={hasTabs} /> )}
comment}} +{{{signatureComment}}} {{/if}} diff --git a/www/utils/packages/utils/src/step-utils.ts b/www/utils/packages/utils/src/step-utils.ts index 38a0ac31b77cc..eda89c6159465 100644 --- a/www/utils/packages/utils/src/step-utils.ts +++ b/www/utils/packages/utils/src/step-utils.ts @@ -3,9 +3,16 @@ import { ArrayType, SignatureReflection, SomeType, UnionType } from "typedoc" const disallowedIntrinsicTypeNames = ["unknown", "void", "any", "never"] export function isWorkflowStep(reflection: SignatureReflection): boolean { - return ( - reflection.parent?.children?.some((child) => child.name === "__step__") || - false + if (reflection.parent?.children?.some((child) => child.name === "__step__")) { + return true + } + if (reflection.type?.type !== "intersection") { + return false + } + return reflection.type.types.some( + (refType) => + refType.type === "reference" && + refType.name === "StepFunctionReturnConfig" ) }