Gift cards post mvp (#1632)

* Add gift bulk create (#1490)

* Add gift card bulk create dialog WIP

* Update schema, add gift card bulk create mutation and types

* Add gift card bulk create modal and mutation

* Fix types and update messages

* Refactor gift card bulk create

* Add closing gift card bulk create dialog after success

* Change gift card list closeDialog prop name to onClose

* Review fixes

* Review fixes

* Add error message to invalid expiryDate (#1518)

* Add error message to invalid expiryDate

* Add default messages

* Replace filter with some

* Add gift card export (#1499)

* wip

* Add exporting gift cards

* Update messages

* Fix types

* Review fixes

* Review fixes

* Refactor passing messages to export settings component

* Refactor

* Update messages

* Gift card customer page (#1520)

* Add gift cards card to customer page (#1456)

* WIP

* WIP

* Extract activate/deactivate logic to a hook

* add optional side action

* Add query for customer's gift cards

* Add component for giftcard status chip

* Graphql run types

* Add gift card card to customer page

* Fix status chip header

* Revert style change

* Unify status chip logic

* Fix naming scheme

* Add currentOpts to act/deactivate gift cards hook

* Add queries to refetch prop

* Simplify gift card list component

* Fix order status chip

* Extract messages to separate file

* Remove unused lines of code

* Tests and messages

* Fix card list rendering

* Type fix

* Code review fixes

* Review changes

* Scripts

* Change variable name

* Fix formatted message

* Check if giftcards exist before rendering collection

* Add loading button to CardMenu component (#1476)

* WIP

* WIP

* Add gift card card to customer page

* Fix status chip header

* Fix naming scheme

* Add currentOpts to act/deactivate gift cards hook

* Remove unused lines of code

* Revert style change

* Tests and messages

* Fix card list rendering

* Type fix

* Code review fixes

* Review changes

* Scripts

* Add loading animation to card menu buttons

* Added default messages

* Change conditional prop checking to filtering

* Issue gift card in customer page (#1468)

* WIP

* WIP

* Replace typed query with make query

* Add customer details context to customer page

* Add context to customer gift cards

* Disable customer select when initial customer is present

* Pass initial customer to create gift card form

* Fixes after cherry-pick

* Code cleanup

* Remove getInitialData function

* Remove unused package

* Remove new line

* Post-rebase fixes

* Code cleanup & extract messages

* Remove unused code

* Create customer details hook

* Minor fixes

* Update default messages

* Update gift card types

* Type fixes

* Change directory of useCustomerDetails hook

* CR Fixes

* Update tests

* Make PageTitleWithStatusChip use ExtendedPageHeader

* Update tests

* Update hook name

* Post-rebase fixes

* Eslint fix

* Fix scrollbar appearing in menu issue (#1539)

* Change displayCode to last4CodeChars (#1573)

* Add filtered redirect to gift card page from customer details (#1556)

* Limit number of channel list items (#1607)

* Add max height to single select field menu items container

* Add storybook case

* Update stories tests

* Fix gift card product changing to preorder on save issue (#1583)

* Variant preorder fix

* Global threshold input should be optional

* update snapshot

* Add displaying logic gift cards list toolbar (#1617)

* add displaying logic for gift card toolbar

* Logic fix

* Handle bulk export after creation (#1544)

* Add gift bulk create (#1490)

* Add gift card bulk create dialog WIP

* Update schema, add gift card bulk create mutation and types

* Add gift card bulk create modal and mutation

* Fix types and update messages

* Refactor gift card bulk create

* Add closing gift card bulk create dialog after success

* Change gift card list closeDialog prop name to onClose

* Review fixes

* Review fixes

* Add gift card export (#1499)

* wip

* Add exporting gift cards

* Update messages

* Fix types

* Review fixes

* Review fixes

* Refactor passing messages to export settings component

* Refactor

* Update messages

* Handle export after bulk gift card creation

* Add default messages

* Create an util function to get correct input for export

* Update component's name

* Change modal's title

* Update messages

Co-authored-by: Magdalena Markusik <magdalena@markusik.com>

* Gift Card List item number change doesn't refetch fix (#1643)

* Fix number of rows change not refetching list

* Add pagination reset

* Update descriptions for gift card messages to be more descriptive (#1648)

* Gift card history timeline  (#1597)

* Update queries and mutations

* Allow title to be a react node

* Show user name if it exists in data object

* Update types

* Refresh queries on note add

* Add gift card history

* type fix

* Update messages

* Fix timeline note

* Add event fragment to form update result

* Update types

* Fix typo

* Update messages

* Disable input if gift card is expired

* Remove unused imports

* CR Fixes

* Change messages location

* Change message to include user in order

* Allow adding notes to expired gift cards

* Fix disabled input showing dropdown issue (#1636)

* On click is disabled when component is disabled

* update tests

* Order filtering for gift cards in Order List View (#1628)

* Add gift card order filter

* Add gift card filter card in orders view

* Bump macaw version

* Update messages

* Update tests

* Code review changes

* update messages

* Change info card message to use only one message

* Fix order gift card filter

* Gift card filter in product list view (#1621)

* Add GiftCard or Normal filter in Product List View

* Fix tests

* Fix type check

* Don't filter if query param is not in enum

* Update messages

* Update tests

* Code cleanup

* Add default messages

* Pass intl rather than initialise it in util

* Post-rebase fixes

* Change product type details messages (#1642)

* Update product type gift card options messages

* update tests

* Add sorting to gift card list (#1569)

* Update queries and types for sorting

* Add optional handleError method to makeQuery

* Add sorting to gift card list

* Sorting hook uses useGiftCardList hook

* Convert to boolean

* Add default sorting field

* format fix

* Add expiry error handling for issuing gift cards (#1634)

* Add expiry error handling for issuing gift cards

* Add expiry error message to gift card bulk issue

* Update Gift Card tag queries to utilise multiple tags (#1685)

* Change displayCode to last4CodeChars (#1573)

* Update types

* WIP

* Update gift card forms to utilise multiple tags

* Code cleanup

* Update gift card event types

* Fixes

* Change column with no click handler behaviour

* Remove an ability to sort by tags

* Remove unused code

* Update tests

* Update timeline events

* Update messages

* change array reduce to join

* Add Y scroll to dialog content

* Bulk create Y scroll fix

* Endless loading fix (#1732)

* Order filtering for gift cards in Order List View (#1628)

* Add gift card order filter

* Add gift card filter card in orders view

* Bump macaw version

* Update messages

* Update tests

* Code review changes

* update messages

* Change info card message to use only one message

* Fix order gift card filter

* Wip

* Extract dialog component outside of gift card create

* Update component's name

* Extract dialog component outside of export component to provider

* Update tests

* Various gift card bugs fixes (#1749)

* Change currency to options field

* Fix hover and font size

* Fix gift card list width

* Fix bulk delete dialog

* Allow balance to be sorted only when currency is filtered

* Sorting by balance after removing currency filter defualts to usedBy

* Fix trash icon

* Add filter dependency

* Fix single deletion with bulk

* Update tests

* Refactor Links used in Gift Cards

* Fix export dialog (#1791)

Co-authored-by: Magdalena Markusik <magdalena@markusik.com>
This commit is contained in:
Wojciech Mista 2022-01-25 13:44:19 +01:00 committed by GitHub
parent 53a391eebc
commit 8f9c1ba19e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
172 changed files with 7497 additions and 2105 deletions

View file

@ -131,6 +131,9 @@
{ {
"name": "GiftCardEvent" "name": "GiftCardEvent"
}, },
{
"name": "GiftCardTag"
},
{ {
"name": "Group" "name": "Group"
}, },

View file

@ -1966,6 +1966,10 @@
"context": "unassign multiple attributes from item", "context": "unassign multiple attributes from item",
"string": "{counter,plural,one{Are you sure you want to unassign this attribute from {itemTypeName}?} other{Are you sure you want to unassign {attributeQuantity} attributes from {itemTypeName}?}}" "string": "{counter,plural,one{Are you sure you want to unassign this attribute from {itemTypeName}?} other{Are you sure you want to unassign {attributeQuantity} attributes from {itemTypeName}?}}"
}, },
"src_dot_components_dot_CardMenu_dot_cardMenuItemLoading": {
"context": "menu item loading",
"string": "working..."
},
"src_dot_components_dot_ChannelsAvailabilityCard_dot_3326160357": { "src_dot_components_dot_ChannelsAvailabilityCard_dot_3326160357": {
"context": "section header", "context": "section header",
"string": "Availability" "string": "Availability"
@ -3509,215 +3513,336 @@
"context": "gift cards section name", "context": "gift cards section name",
"string": "Gift Cards" "string": "Gift Cards"
}, },
"src_dot_giftCards_dot_GiftCardBulkCreateDialog_dot_createdSuccessAlertDescription": {
"context": "bulk issue gift cards success alert description",
"string": "Requested {cardsAmount} were successfully issued"
},
"src_dot_giftCards_dot_GiftCardBulkCreateDialog_dot_createdSuccessAlertTitle": {
"context": "bulk issue gift cards success alert title",
"string": "Gift Cards Issued"
},
"src_dot_giftCards_dot_GiftCardBulkCreateDialog_dot_title": {
"context": "bulk issue gift cards dialog title",
"string": "Bulk Issue Gift Cards"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryDateLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryDateLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label", "context": "expiry date label",
"string": "Exact date" "string": "Exact date"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryOnLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryOnLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry on label", "context": "expires on label",
"string": "Will expire on:" "string": "Will expire on:"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryPeriodLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryPeriodLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry period label", "context": "expires in label",
"string": "Expires in" "string": "Expires in"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expirySelectedLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expirySelectedLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry selected label", "context": "set expiry date selected label",
"string": "Set gift card expiry date" "string": "Set gift card expiry date"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_amountLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_amountLabel": {
"context": "GiftCardCreateDialog amount label", "context": "money amount input label",
"string": "Enter amount" "string": "Enter amount"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_bulkCreateExplanation": {
"context": "gift card bulk create modal bottom explanation",
"string": "After creation Saleor will create a list of gift card codes that you will be able to download. "
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_bulkCreateIssuedAccept": {
"context": "gift card bulk create success dialog accept button",
"string": "Ok"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_bulkCreateIssuedExplanation": {
"context": "gift card bulk create success dialog content",
"string": "We have issued all of your requested gift cards. You can download the list of new gift cards using the button below."
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_bulkCreateIssuedExportToEmail": {
"context": "gift card bulk create success dialog export button",
"string": "Export To Email"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_bulkCreateIssuedTitle": {
"context": "gift card bulk create success dialog title",
"string": "Bulk Issue Gift Cards"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_copiedToClipboardTitle": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_copiedToClipboardTitle": {
"context": "GiftCardCreateDialog copied to clipboard title", "context": "copied to clipboard alert title",
"string": "Copied to clipboard" "string": "Copied to clipboard"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_copyCodeLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_copyCodeLabel": {
"context": "GiftCardCreateDialog copy code label", "context": "copy code button label",
"string": "Copy code" "string": "Copy code"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_createdGiftCardLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_createdGiftCardLabel": {
"context": "GiftCardCreateDialog created gift card label", "context": "created gift card code label",
"string": "This is the code of a created gift card:" "string": "This is the code of a created gift card:"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_createdSuccessAlertTitle": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_createdSuccessAlertTitle": {
"context": "GiftCardCreateDialog createdSuccessAlertTitle", "context": "successfully created gift card alert title",
"string": "Successfully created gift card" "string": "Successfully created gift card"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_customerLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_customerLabel": {
"context": "GiftCardCreateDialog customer label", "context": "customer input label",
"string": "Customer" "string": "Customer"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_giftCardsAmountLabel": {
"context": "issued cards amount label",
"string": "Cards Issued"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_issueButtonLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_issueButtonLabel": {
"context": "GiftCardCreateDialog issue button label", "context": "issue gift card button label",
"string": "Issue" "string": "Issue"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_noteLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_noteLabel": {
"context": "GiftCardCreateDialog note label", "context": "note input label",
"string": "Note" "string": "Note"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_noteSubtitle": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_noteSubtitle": {
"context": "GiftCardCreateDialog note subtitle", "context": "note input subtitle",
"string": "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history" "string": "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationCaption": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationCaption": {
"context": "GiftCarUpdateDetailsExpirySection requires activation caption", "context": "requires activation checkbox caption",
"string": "All issued cards require activation by staff before use." "string": "All issued cards require activation by staff before use."
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationLabel": {
"context": "GiftCarUpdateDetailsExpirySection requires activation label", "context": "requires activation checkbox label",
"string": "Requires activation" "string": "Requires activation"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_title": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_title": {
"context": "GiftCardCreateDialog title", "context": "issue gift card dialog title",
"string": "Issue gift card" "string": "Issue gift card"
}, },
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_2086397658": {
"context": "export all items to csv file",
"string": "All gift cards ({number})"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_701638571": {
"context": "export selected items to csv file",
"string": "Selected giftCards ({number})"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_confirmButtonLabel": {
"context": "gift card export dialog confirm button label",
"string": "Export codes"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_exportNote": {
"context": "note on export gift cards",
"string": "Note: Only active and not used gift cards will be expored"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_exportTypeLabel": {
"context": "gift card export type label",
"string": "gift cards"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_successAlertDescription": {
"context": "gift card export success alert description",
"string": "We are currently exporting your gift card codes. As soon as your file is available it will be sent to your email address"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_successAlertTitle": {
"context": "gift card export csv success alert title",
"string": "Exporting CSV"
},
"src_dot_giftCards_dot_GiftCardExportDialogContent_dot_title": {
"context": "gift card export dialog title",
"string": "Export Gift Card Codes"
},
"src_dot_giftCards_dot_GiftCardSettings_dot_GiftCardExpirySettingsCard_dot_expiryDateSectionDescription": { "src_dot_giftCards_dot_GiftCardSettings_dot_GiftCardExpirySettingsCard_dot_expiryDateSectionDescription": {
"context": "expiry date selection info message",
"string": "You can set gift cards to expire after a certain time after their purchase. Remember that in some countries, gift cards expiry is prohibited by law." "string": "You can set gift cards to expire after a certain time after their purchase. Remember that in some countries, gift cards expiry is prohibited by law."
}, },
"src_dot_giftCards_dot_GiftCardSettings_dot_GiftCardExpirySettingsCard_dot_expiryDateTitle": { "src_dot_giftCards_dot_GiftCardSettings_dot_GiftCardExpirySettingsCard_dot_expiryDateTitle": {
"context": "section header", "context": "expiry date section header",
"string": "Expiry date" "string": "Expiry date"
}, },
"src_dot_giftCards_dot_GiftCardSettings_dot_title": { "src_dot_giftCards_dot_GiftCardSettings_dot_title": {
"context": "header", "context": "gift card settings header",
"string": "Gift Cards Settings" "string": "Gift Cards Settings"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardActivated": {
"context": "gift card history message",
"string": "Gift card was activated by {activatedBy}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardBalanceReset": {
"context": "gift card history message",
"string": "Gift card balance was reset by {resetBy}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardBought": {
"context": "gift card history message",
"string": "Gift card was bought in order {orderNumber}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardDeactivated": {
"context": "gift card history message",
"string": "Gift card was deactivated by {deactivatedBy}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardExpiryDateUpdate": {
"context": "gift card history message",
"string": "Gift card expiry date was updated by {expiryUpdatedBy}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardIssued": {
"context": "dsc",
"string": "Gift card was issued by {issuedBy}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardResent": {
"context": "gift card history message",
"string": "Gift card was resent"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardSentToCustomer": {
"context": "gift card history message",
"string": "Gift card was sent to customer"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardTagsUpdated": {
"context": "gift card history message",
"string": "Gift card tags were updated"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_giftCardUsedInOrder": {
"context": "gift card history message",
"string": "Gift card was used as a payment method on order {orderLink} <buyer>by</buyer>"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_historyHeaderTitle": {
"context": "section header title",
"string": "Gift Card Timeline"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_noteAddError": {
"context": "notifier message",
"string": "There was an error adding a note"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardHistory_dot_noteAddedSuccessfully": {
"context": "notifier message",
"string": "Note was added sucessfully"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_consentCheckboxLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_consentCheckboxLabel": {
"context": "GiftCardResendCodeDialog consentCheckboxLabel", "context": "consent to send gift card to different address checkbox label",
"string": "Yes, I want to send gift card to different address" "string": "Yes, I want to send gift card to different address"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_description": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_description": {
"context": "GiftCardResendCodeDialog description", "context": "resend code to customer description",
"string": "Gift Card Code will be resent to email provided during checkout. You can provide a different email address if you want to:" "string": "Gift Card Code will be resent to email provided during checkout. You can provide a different email address if you want to:"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_emailInputPlaceholder": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_emailInputPlaceholder": {
"context": "GiftCardResendCodeDialog emailInputPlaceholder", "context": "provided email input placeholder",
"string": "Provided email address" "string": "Provided email address"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_sendToChannelSelectLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_sendToChannelSelectLabel": {
"context": "ChannelPickerSelectField sendToChannelLabel", "context": "send to channel select label",
"string": "Send to channel" "string": "Send to channel"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_submitButtonLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_submitButtonLabel": {
"context": "GiftCardResendCodeDialog submitButtonLabel", "context": "resend button label",
"string": "Resend" "string": "Resend"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_successResendAlertText": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_successResendAlertText": {
"context": "GiftCardResendCodeDialog successResendAlertText", "context": "resent code success message",
"string": "Successfully resent code to customer!" "string": "Successfully resent code to customer!"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_title": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_title": {
"context": "GiftCardResendCodeDialog title", "context": "resend code to customer title",
"string": "Resend code to customer" "string": "Resend code to customer"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_changeButtonLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_changeButtonLabel": {
"context": "GiftCardUpdateDetailsCard set balance dialog change button label", "context": "change button label",
"string": "Change" "string": "Change"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_subtitle": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_subtitle": {
"context": "GiftCardUpdateDetailsCard set balance dialog subtitle", "context": "set balance dialog subtitle",
"string": "What would you like to set cards balance to. When you change the balance both values will be changed" "string": "What would you like to set cards balance to. When you change the balance both values will be changed"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_title": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_title": {
"context": "GiftCardUpdateDetailsCard set balance button label", "context": "set balance dialog title label",
"string": "Set balance" "string": "Set balance"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_updatedSuccessAlertTitle": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_updatedSuccessAlertTitle": {
"context": "GiftCardUpdateDetailsCard update success alert title", "context": "card update success alert title",
"string": "Successfully updated card balance" "string": "Successfully updated card balance"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_cardBalanceLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_cardBalanceLabel": {
"context": "GiftCardUpdateDetailsCard card balance label", "context": "card balance label",
"string": "Card Balance" "string": "Card Balance"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_setBalanceButtonLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_setBalanceButtonLabel": {
"context": "GiftCardUpdateDetailsCard set balance button label", "context": "set balance button label",
"string": "set balance" "string": "set balance"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_tagInputLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_tagInputLabel": {
"context": "GiftCardTagInput tag label", "context": "tag label",
"string": "Card Tag" "string": "Card Tag"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_title": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_title": {
"context": "GiftCardUpdateDetailsCard title", "context": "details title",
"string": "Details" "string": "Details"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiredOnLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiredOnLabel": {
"context": "GiftCarUpdateDetailsExpirySection expired on label", "context": "expired on label",
"string": "Expired on {date}" "string": "Expired on {date}"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateCheckboxLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateCheckboxLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date checkbox label", "context": "expiry date checkbox label",
"string": "Gift card expires" "string": "Gift card expires"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label", "context": "expiration date label",
"string": "Expiration date" "string": "Expiration date"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_boughtByLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_boughtByLabel": {
"context": "GiftCardUpdateInfoCard bought by label", "context": "bought by label",
"string": "Bought by" "string": "Bought by"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_creationLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_creationLabel": {
"context": "GiftCardUpdateInfoCard creation label", "context": "creation label",
"string": "Creation" "string": "Creation"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_issuedByAppLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_issuedByAppLabel": {
"context": "GiftCardUpdateInfoCard issued by app label", "context": "issued by app label",
"string": "Issued by app" "string": "Issued by app"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_issuedByLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_issuedByLabel": {
"context": "GiftCardUpdateInfoCard issued by label", "context": "issued by label",
"string": "Issued by" "string": "Issued by"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_orderNumberLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_orderNumberLabel": {
"context": "GiftCardUpdateInfoCard order number label", "context": "order number label",
"string": "Order number" "string": "Order number"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_productLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_productLabel": {
"context": "GiftCardUpdateInfoCard product label", "context": "product label",
"string": "Product bought to get gift card" "string": "Product bought to get gift card"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_title": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_title": {
"context": "GiftCardUpdateInfoCard title", "context": "info card title",
"string": "Card information" "string": "Card information"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_usedByLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_usedByLabel": {
"context": "GiftCardUpdateInfoCard used by label", "context": "used by label",
"string": "Used by" "string": "Used by"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_disabledStatusLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_disabledStatusLabel": {
"context": "giftCardUpdatePageHeader disabled status label", "context": "disabled status label",
"string": "Disabled" "string": "Disabled"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_expiredStatusLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_expiredStatusLabel": {
"context": "giftCardUpdatePageHeader expired status label", "context": "expired status label",
"string": "Expired" "string": "Expired"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_resendButtonLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_resendButtonLabel": {
"context": "giftCardUpdatePageHeader resendButtonLabel", "context": "resend code label",
"string": "Resend code" "string": "Resend code"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyDisabledTitle": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyDisabledTitle": {
"context": "GiftCardEnableDisableSection disable success", "context": "success gift card disable message",
"string": "Successfully disabled gift card" "string": "Successfully disabled gift card"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyEnabledTitle": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyEnabledTitle": {
"context": "GiftCardEnableDisableSection enable success", "context": "success gift card enable message",
"string": "Successfully enabled gift card" "string": "Successfully enabled gift card"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_notFound": { "src_dot_giftCards_dot_GiftCardUpdate_dot_notFound": {
"context": "giftCardErrorMessages not found", "context": "gift card not found message",
"string": "Couldn't find gift card" "string": "Couldn't find gift card"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_title": { "src_dot_giftCards_dot_GiftCardUpdate_dot_title": {
"context": "GiftCardUpdateDetailsCard title", "context": "title",
"string": "Details" "string": "Details"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmount": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmount": {
"context": "Filter balance amount error", "context": "balance amound missing error message",
"string": "Balance amount is missing" "string": "Balance amount is missing"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmountLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmountLabel": {
@ -3725,7 +3850,7 @@
"string": "Amount" "string": "Amount"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceCurrency": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceCurrency": {
"context": "Filter balance currency error", "context": "balance curreny missing error message",
"string": "Balance currency is missing" "string": "Balance currency is missing"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_currencyLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_currencyLabel": {
@ -3737,7 +3862,7 @@
"string": "Current balance" "string": "Current balance"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_defaultTabLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_defaultTabLabel": {
"context": "gift card default tab label", "context": "all gift cards label",
"string": "All Gift Cards" "string": "All Gift Cards"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_disabledOptionLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_disabledOptionLabel": {
@ -3757,7 +3882,7 @@
"string": "Product" "string": "Product"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_searchPlaceholder": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_searchPlaceholder": {
"context": "gift card search placeholder", "context": "search gift card placeholder",
"string": "Search Gift Cards, e.g {exampleGiftCardCode}" "string": "Search Gift Cards, e.g {exampleGiftCardCode}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_statusLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_statusLabel": {
@ -3772,136 +3897,172 @@
"context": "used by filter label", "context": "used by filter label",
"string": "Used by" "string": "Used by"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListOrderInfoCard_dot_giftCardOrderInfoMessage": {
"context": "alert card message",
"string": "Gift cards will appear after their order is fullfilled. <link>View Orders with Gift Cards</link>"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_deleteLabel": {
"context": "bulk delete label",
"string": "Delete"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_disableLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_disableLabel": {
"context": "GiftCardEnableDisableSection enable label", "context": "bulk disable label",
"string": "Deactivate" "string": "Deactivate"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_enableLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_enableLabel": {
"context": "GiftCardEnableDisableSection enable label", "context": "bulk activate label",
"string": "Activate" "string": "Activate"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_errorActivateAlertText": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_errorActivateAlertText": {
"context": "GiftCardEnableDisableSection error activate alert text", "context": "error with activatation alert message",
"string": "Error activating gift {count,plural,one{card} other{cards}}" "string": "Error activating gift {count,plural,one{card} other{cards}}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_errorDeactivateAlertText": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_errorDeactivateAlertText": {
"context": "GiftCardEnableDisableSection error activate alert text", "context": "error with deactivatation alert message",
"string": "Errors deactivating gift {count,plural,one{card} other{cards}}" "string": "Errors deactivating gift {count,plural,one{card} other{cards}}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_successActivateAlertText": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_successActivateAlertText": {
"context": "GiftCardEnableDisableSection success activate alert text", "context": "success activate alert message",
"string": "Successfully activated gift {count,plural,one{card} other{cards}}" "string": "Successfully activated gift {count,plural,one{card} other{cards}}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_successDeactivateAlertText": { "src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_successDeactivateAlertText": {
"context": "GiftCardEnableDisableSection success activate alert text", "context": "success deactivate alert message",
"string": "Successfully deactivated gift {count,plural,one{card} other{cards}}" "string": "Successfully deactivated gift {count,plural,one{card} other{cards}}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_bulkIssue": { "src_dot_giftCards_dot_GiftCardsList_dot_bulkIssue": {
"context": "GiftCardsListHeader menu item settings", "context": "bulk issue menu item",
"string": "Bulk Issue" "string": "Bulk Issue"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_codeEndingWithLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_codeEndingWithLabel": {
"context": "GiftCardsListTable code ending with label", "context": "code ending with label",
"string": "Code ending with {displayCode}" "string": "Code ending with {last4CodeChars}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProduct": { "src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProduct": {
"context": "GiftCardsListHeader alert", "context": "create gift card product alert message",
"string": "Create a gift card product" "string": "Create a gift card product"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProductType": { "src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProductType": {
"context": "GiftCardsListHeader alert", "context": "create gift card product type alert message",
"string": "Create a gift card product type" "string": "Create a gift card product type"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_exportCodes": { "src_dot_giftCards_dot_GiftCardsList_dot_exportCodes": {
"context": "GiftCardsListHeader menu item settings", "context": "export card codes menu item",
"string": "Export card codes" "string": "Export card codes"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardInvalidExpiryDateContent": {
"context": "invalid date in expirydate field content",
"string": "Gift Card with past expiration date cannot be created"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardInvalidExpiryDateHeader": {
"context": "invalid date in expirydate field header",
"string": "Incorrect date entered"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardProduct": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardProduct": {
"context": "GiftCardsListHeader alert", "context": "gift card product message",
"string": "gift card product" "string": "gift card product"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnBalanceTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnBalanceTitle": {
"context": "GiftCardsListTable column title balance", "context": "column title balance",
"string": "Balance" "string": "Balance"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnCustomerTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnCustomerTitle": {
"context": "GiftCardsListTable column title customer", "context": "column title used by/customer",
"string": "Used by" "string": "Used by"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnGiftCardTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnGiftCardTitle": {
"context": "GiftCardsListTable column title gift card", "context": "column title gift card",
"string": "Gift Card" "string": "Gift Card"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnProductTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnProductTitle": {
"context": "GiftCardsListTable column title product", "context": "column title product",
"string": "Product" "string": "Product"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnTagTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnTagTitle": {
"context": "GiftCardsListTable column title tag", "context": "column title tag",
"string": "Tag" "string": "Tag"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_issueButtonLabel": { "src_dot_giftCards_dot_GiftCardsList_dot_issueButtonLabel": {
"context": "GiftCardsListHeader issue button label", "context": "issue card button label",
"string": "Issue card" "string": "Issue card"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsAlertTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsAlertTitle": {
"context": "GiftCardsListHeader alert", "context": "no card defuned alert message",
"string": "You havent defined a gift card product!" "string": "You havent defined a gift card product!"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsFound": { "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsFound": {
"context": "GiftCardsListTable no cards found title", "context": "no cards found title message",
"string": "No gift cards found" "string": "No gift cards found"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProductTypes": { "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProductTypes": {
"context": "GiftCardsListHeader alert", "context": "no gift card product types alert message",
"string": "{createGiftCardProductType} to start selling gift cards in your store." "string": "{createGiftCardProductType} to start selling gift cards in your store."
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProducts": { "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProducts": {
"context": "GiftCardsListHeader alert", "context": "no gift card products alert message",
"string": "{createGiftCardProduct} to start selling gift cards in your store." "string": "{createGiftCardProduct} to start selling gift cards in your store."
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProductsAndProductTypes": { "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProductsAndProductTypes": {
"context": "GiftCardsListHeader alert", "context": "no gift card products and types alert message",
"string": "{createGiftCardProductType} and {giftCardProduct} to start selling gift cards in your store." "string": "{createGiftCardProductType} and {giftCardProduct} to start selling gift cards in your store."
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_settings": { "src_dot_giftCards_dot_GiftCardsList_dot_settings": {
"context": "GiftCardsListHeader menu item settings", "context": "settings menu item",
"string": "Settings" "string": "Settings"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsAbsentSubtitle": {
"context": "customer gift cards card no cards subtitle",
"string": "There are no gift cards assigned to this customer"
},
"src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsCardTitle": {
"context": "customer gift cards card title",
"string": "Gift Cards"
},
"src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsIssueNewCardButton": {
"context": "customer gift cards card issue button",
"string": "Issue new card"
},
"src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsPresentSubtitle": {
"context": "customer gift cards card subtitle",
"string": "Only five newest gift cards are shown here"
},
"src_dot_giftCards_dot_components_dot_GiftCardCustomerCard_dot_customerGiftCardsViewAllButton": {
"context": "customer gift cards card view all button",
"string": "View All"
},
"src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_consentLabel": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_consentLabel": {
"context": "GiftCardDeleteDialog consent label", "context": "consent removal of gift cards with balance button label",
"string": "{selectedItemsCount,plural,one{I am aware that I am removing a gift card with balance} other{I am aware that I am removing gift cards with balance}}" "string": "{selectedItemsCount,plural,one{I am aware that I am removing a gift card with balance} other{I am aware that I am removing gift cards with balance}}"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_defaultDescription": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_defaultDescription": {
"context": "GiftCardDeleteDialog default description", "context": "default gift card delete description",
"string": "{selectedItemsCount,plural,one{Are you sure you want to delete this gift card?} other{Are you sure you want to delete {selectedItemsCount} giftCards?}}" "string": "{selectedItemsCount,plural,one{Are you sure you want to delete this gift card?} other{Are you sure you want to delete {selectedItemsCount} giftCards?}}"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_deleteSuccessAlertText": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_deleteSuccessAlertText": {
"context": "GiftCardDeleteDialog success alert text", "context": "gift card removed success alert message",
"string": "{selectedItemsCount,plural,one{Successfully deleted gift card} other{Successfully deleted gift cards}}" "string": "{selectedItemsCount,plural,one{Successfully deleted gift card} other{Successfully deleted gift cards}}"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_title": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_title": {
"context": "GiftCardDeleteDialog single title", "context": "single gift card title",
"string": "{selectedItemsCount,plural,one{Delete Gift Card} other{Delete Gift Cards}}" "string": "{selectedItemsCount,plural,one{Delete Gift Card} other{Delete Gift Cards}}"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_withBalanceDescription": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_withBalanceDescription": {
"context": "GiftCardDeleteDialog with balance description", "context": "delete gift cards with balance description",
"string": "{selectedItemsCount,plural,one{The gift card you are about to delete has available balance. By deleting this card you may remove balance available to your customer.} other{You are about to delete gift cards with available balance. Are you sure you want to do that?}}" "string": "{selectedItemsCount,plural,one{The gift card you are about to delete has available balance. By deleting this card you may remove balance available to your customer.} other{You are about to delete gift cards with available balance. Are you sure you want to do that?}}"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_channelSelectLabel": { "src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_channelSelectLabel": {
"context": "GiftCardCreateDialog channel select label", "context": "channel select label",
"string": "Channel" "string": "Channel"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_customerChannelSubtitle": { "src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_customerChannelSubtitle": {
"context": "GiftCardCreateDialog customer channel subtitle", "context": "selected customer channel subtitle",
"string": "Customer will be sent the gift card code via this channels email address" "string": "Customer will be sent the gift card code via this channels email address"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_customerSubtitle": { "src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_customerSubtitle": {
"context": "GiftCardCreateDialog customer subtitle", "context": "selected customer gift card is sent to subtitle",
"string": "Selected customer will be sent the generated gift card code. Someone else can redeem the gift card code. Gift card will be assigned to account which redeemed the code." "string": "Selected customer will be sent the generated gift card code. Someone else can redeem the gift card code. Gift card will be assigned to account which redeemed the code."
}, },
"src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_sendToCustomerSelectedLabel": { "src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_sendToCustomerSelectedLabel": {
"context": "GiftCardSendToCustomer send to customer selected label", "context": "send to customer selected label",
"string": "Send gift card to customer" "string": "Send gift card to customer"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardSettingsExpirySelect_dot_setExpirationPeriodDescription": { "src_dot_giftCards_dot_components_dot_GiftCardSettingsExpirySelect_dot_setExpirationPeriodDescription": {
@ -3913,23 +4074,23 @@
"string": "Set gift card expiration period" "string": "Set gift card expiration period"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardTagInput_dot_placeholder": { "src_dot_giftCards_dot_components_dot_GiftCardTagInput_dot_placeholder": {
"context": "GiftCardTagInput tag placeholder", "context": "input placeholder tag",
"string": "Tag" "string": "Tag"
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_dayLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_dayLabel": {
"context": "TimePeriodTextWithSelectField day label", "context": "days after label",
"string": "days after issue" "string": "days after issue"
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_monthLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_monthLabel": {
"context": "TimePeriodTextWithSelectField month label", "context": "months after label",
"string": "months after issue" "string": "months after issue"
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_weekLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_weekLabel": {
"context": "TimePeriodTextWithSelectField day label", "context": "weeks after label",
"string": "weeks after issue" "string": "weeks after issue"
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_yearLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_yearLabel": {
"context": "TimePeriodTextWithSelectField year label", "context": "years after label",
"string": "years after issue" "string": "years after issue"
}, },
"src_dot_home": { "src_dot_home": {
@ -4659,6 +4820,18 @@
"context": "order", "context": "order",
"string": "Customer" "string": "Customer"
}, },
"src_dot_orders_dot_components_dot_OrderListPage_dot_giftCard": {
"context": "order",
"string": "Gift Card"
},
"src_dot_orders_dot_components_dot_OrderListPage_dot_giftCardOrdered": {
"context": "order",
"string": "Gift Card ordered"
},
"src_dot_orders_dot_components_dot_OrderListPage_dot_giftCardPaid": {
"context": "order",
"string": "Paid with Gift Card"
},
"src_dot_orders_dot_components_dot_OrderListPage_dot_placed": { "src_dot_orders_dot_components_dot_OrderListPage_dot_placed": {
"context": "order", "context": "order",
"string": "Created" "string": "Created"
@ -5790,19 +5963,15 @@
}, },
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionGiftCardDescription": { "src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionGiftCardDescription": {
"context": "option description", "context": "option description",
"string": "This product type can be used to create voucher/gift card type products that user will be able to pay with during checkout process" "string": "This product will act as a payment method"
}, },
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionGiftCardTitle": { "src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionGiftCardTitle": {
"context": "option", "context": "option",
"string": "Products of this type can be used as gift card/voucher" "string": "Gift card product type"
},
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionNormalDescription": {
"context": "option description",
"string": "This product type can be used to create shipping passes for your customers"
}, },
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionNormalTitle": { "src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionNormalTitle": {
"context": "option", "context": "option",
"string": "This is a normal product" "string": "Regular product type"
}, },
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_productTypeName": { "src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_productTypeName": {
"context": "label", "context": "label",
@ -5985,15 +6154,15 @@
"string": "SEO Information" "string": "SEO Information"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2167661409": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2167661409": {
"context": "export selected products to csv file", "context": "export selected items to csv file",
"string": "Selected products ({number})" "string": "Selected products ({number})"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2318723509": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2318723509": {
"context": "export products to csv file, choice field label", "context": "export items to csv file, choice field label",
"string": "Export information for:" "string": "Export information for:"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2355065897": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2355065897": {
"context": "export all products to csv file", "context": "export all items to csv file",
"string": "All products ({number})" "string": "All products ({number})"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2474350154": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2474350154": {
@ -6005,17 +6174,9 @@
"string": "Information exported" "string": "Information exported"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2693217446": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2693217446": {
"context": "export products as csv or spreadsheet file", "context": "export items as csv or spreadsheet file",
"string": "Export as:" "string": "Export as:"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2883720012": {
"context": "export products to csv file, button",
"string": "export products"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3012202273": {
"context": "export products to csv file, dialog header",
"string": "Export Information"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3365843236": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3365843236": {
"context": "product export to csv file, header", "context": "product export to csv file, header",
"string": "Export Settings" "string": "Export Settings"
@ -6025,7 +6186,7 @@
"string": "Select All" "string": "Select All"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3518309850": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3518309850": {
"context": "export products as spreadsheet", "context": "export items as spreadsheet",
"string": "Spreadsheet for Excel, Numbers etc." "string": "Spreadsheet for Excel, Numbers etc."
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3599582104": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3599582104": {
@ -6039,7 +6200,7 @@
"string": "Inventory Information" "string": "Inventory Information"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_4118932547": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_4118932547": {
"context": "export products as csv file", "context": "export items as csv file",
"string": "Plain CSV file" "string": "Plain CSV file"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_472026385": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_472026385": {
@ -6051,13 +6212,25 @@
"string": "Search by attribute name" "string": "Search by attribute name"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_700651641": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_700651641": {
"context": "export filtered products to csv file", "context": "export filtered items to csv file",
"string": "Current search ({number})" "string": "Current search ({number})"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_734825715": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_734825715": {
"context": "informations about product prices etc, header", "context": "informations about product prices etc, header",
"string": "Financial Information" "string": "Financial Information"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_confirmButtonLabel": {
"context": "export products to csv file, button",
"string": "export products"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_productsLabel": {
"context": "products export type label",
"string": "products"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_title": {
"context": "export products to csv file, dialog header",
"string": "Export Information"
},
"src_dot_products_dot_components_dot_ProductExternalMediaDialog_dot_4146081479": { "src_dot_products_dot_components_dot_ProductExternalMediaDialog_dot_4146081479": {
"context": "modal header", "context": "modal header",
"string": "Media from the URL you supply will be shown in the media gallery. You will be able to define the order of the gallery." "string": "Media from the URL you supply will be shown in the media gallery. You will be able to define the order of the gallery."
@ -6104,6 +6277,10 @@
"context": "product is hidden", "context": "product is hidden",
"string": "Hidden" "string": "Hidden"
}, },
"src_dot_products_dot_components_dot_ProductListPage_dot_kind": {
"context": "product kind",
"string": "Product Kind"
},
"src_dot_products_dot_components_dot_ProductListPage_dot_outOfStock": { "src_dot_products_dot_components_dot_ProductListPage_dot_outOfStock": {
"context": "product status", "context": "product status",
"string": "Out Of Stock" "string": "Out Of Stock"
@ -6716,6 +6893,14 @@
"src_dot_products_dot_views_dot_ProductList_dot_44832327": { "src_dot_products_dot_views_dot_ProductList_dot_44832327": {
"string": "We are currently exporting your requested CSV. As soon as it is available it will be sent to your email address" "string": "We are currently exporting your requested CSV. As soon as it is available it will be sent to your email address"
}, },
"src_dot_products_dot_views_dot_ProductList_dot_giftCardLabel": {
"context": "label",
"string": "Gift Card"
},
"src_dot_products_dot_views_dot_ProductList_dot_normalLabel": {
"context": "label",
"string": "Normal"
},
"src_dot_products_dot_views_dot_ProductUpdate_dot_3423943948": { "src_dot_products_dot_views_dot_ProductUpdate_dot_3423943948": {
"string": "Manage Products Channel Availability" "string": "Manage Products Channel Availability"
}, },

17
package-lock.json generated
View file

@ -6413,23 +6413,6 @@
"requires": { "requires": {
"@types/react": "*", "@types/react": "*",
"hoist-non-react-statics": "^3.3.0" "hoist-non-react-statics": "^3.3.0"
},
"dependencies": {
"@types/react": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz",
"integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==",
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"csstype": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
"integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g=="
}
} }
}, },
"@types/html-minifier-terser": { "@types/html-minifier-terser": {

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,32 @@
import { import {
CircularProgress,
ClickAwayListener, ClickAwayListener,
Grow, Grow,
IconButton, IconButton,
MenuItem, MenuItem,
MenuList, MenuList,
Paper, Paper,
Popper Popper,
Typography
} from "@material-ui/core"; } from "@material-ui/core";
import MoreVertIcon from "@material-ui/icons/MoreVert"; import MoreVertIcon from "@material-ui/icons/MoreVert";
import { makeStyles } from "@saleor/macaw-ui"; import { makeStyles } from "@saleor/macaw-ui";
import React from "react"; import classNames from "classnames";
import * as React from "react";
import { useEffect, useRef, useState } from "react";
import { FormattedMessage } from "react-intl";
import { cardMenuMessages as messages } from "./messages";
const ITEM_HEIGHT = 48; const ITEM_HEIGHT = 48;
export interface CardMenuItem { export interface CardMenuItem {
disabled?: boolean; disabled?: boolean;
label: string; label: string;
testId?: string; testId?: string;
onSelect: () => void; onSelect: () => void;
loading?: boolean;
withLoading?: boolean;
hasError?: boolean;
} }
export interface CardMenuProps { export interface CardMenuProps {
@ -42,6 +51,14 @@ const useStyles = makeStyles(
marginTop: theme.spacing(2), marginTop: theme.spacing(2),
maxHeight: ITEM_HEIGHT * 4.5, maxHeight: ITEM_HEIGHT * 4.5,
overflowY: "scroll" overflowY: "scroll"
},
loadingContent: {
width: "100%",
display: "grid",
gridTemplateColumns: "1fr 24px",
gap: theme.spacing(2),
alignItems: "center",
justifyContent: "flex-end"
} }
}), }),
{ name: "CardMenu" } { name: "CardMenu" }
@ -51,8 +68,8 @@ const CardMenu: React.FC<CardMenuProps> = props => {
const { className, disabled, menuItems, ...rest } = props; const { className, disabled, menuItems, ...rest } = props;
const classes = useStyles(props); const classes = useStyles(props);
const anchorRef = React.useRef<HTMLButtonElement | null>(null); const anchorRef = useRef<HTMLButtonElement | null>(null);
const [open, setOpen] = React.useState(false); const [open, setOpen] = useState(false);
const handleToggle = () => setOpen(prevOpen => !prevOpen); const handleToggle = () => setOpen(prevOpen => !prevOpen);
@ -74,8 +91,8 @@ const CardMenu: React.FC<CardMenuProps> = props => {
} }
}; };
const prevOpen = React.useRef(open); const prevOpen = useRef(open);
React.useEffect(() => { useEffect(() => {
if (prevOpen.current === true && open === false) { if (prevOpen.current === true && open === false) {
anchorRef.current!.focus(); anchorRef.current!.focus();
} }
@ -83,11 +100,27 @@ const CardMenu: React.FC<CardMenuProps> = props => {
prevOpen.current = open; prevOpen.current = open;
}, [open]); }, [open]);
useEffect(() => {
const hasAnyItemsLoadingOrWithError = menuItems
?.filter(({ withLoading }) => withLoading)
?.some(({ loading, hasError }) => loading || hasError);
if (!hasAnyItemsLoadingOrWithError) {
setOpen(false);
}
}, [menuItems]);
const handleMenuClick = (index: number) => { const handleMenuClick = (index: number) => {
menuItems[index].onSelect(); const selectedItem = menuItems[index];
setOpen(false); selectedItem.onSelect();
if (!selectedItem.withLoading) {
setOpen(false);
}
}; };
const isWithLoading = menuItems.some(({ withLoading }) => withLoading);
return ( return (
<div className={className} {...rest}> <div className={className} {...rest}>
<IconButton <IconButton
@ -115,7 +148,8 @@ const CardMenu: React.FC<CardMenuProps> = props => {
{...TransitionProps} {...TransitionProps}
style={{ style={{
transformOrigin: transformOrigin:
placement === "bottom" ? "right top" : "right bottom" placement === "bottom" ? "right top" : "right bottom",
overflowY: "auto"
}} }}
> >
<Paper className={classes.paper}> <Paper className={classes.paper}>
@ -128,12 +162,29 @@ const CardMenu: React.FC<CardMenuProps> = props => {
{menuItems.map((menuItem, menuItemIndex) => ( {menuItems.map((menuItem, menuItemIndex) => (
<MenuItem <MenuItem
data-test-id={menuItem.testId} data-test-id={menuItem.testId}
disabled={menuItem.disabled} disabled={menuItem.loading || menuItem.disabled}
onClick={() => handleMenuClick(menuItemIndex)} onClick={() => handleMenuClick(menuItemIndex)}
key={menuItem.label} key={menuItem.label}
data-test={menuItem.testId} data-test={menuItem.testId}
> >
{menuItem.label} <div
className={classNames(className, {
[classes.loadingContent]: isWithLoading
})}
>
{menuItem.loading ? (
<>
<Typography variant="subtitle1">
<FormattedMessage
{...messages.cardMenuItemLoading}
/>
</Typography>
<CircularProgress size={24} />
</>
) : (
<Typography>{menuItem.label}</Typography>
)}
</div>
</MenuItem> </MenuItem>
))} ))}
</MenuList> </MenuList>

View file

@ -0,0 +1,8 @@
import { defineMessages } from "react-intl";
export const cardMenuMessages = defineMessages({
cardMenuItemLoading: {
defaultMessage: "working...",
description: "menu item loading"
}
});

View file

@ -27,6 +27,9 @@ const useStyles = makeStyles(
fontWeight: 500, fontWeight: 500,
lineHeight: 1 lineHeight: 1
}, },
subtitle: {
fontWeight: 400
},
toolbar: { toolbar: {
marginRight: theme.spacing(-1) marginRight: theme.spacing(-1)
} }
@ -39,6 +42,7 @@ interface CardTitleProps {
className?: string; className?: string;
height?: "default" | "const"; height?: "default" | "const";
title: string | React.ReactNode; title: string | React.ReactNode;
subtitle?: string | React.ReactNode;
toolbar?: React.ReactNode; toolbar?: React.ReactNode;
onClick?: (event: React.MouseEvent<any>) => void; onClick?: (event: React.MouseEvent<any>) => void;
} }
@ -49,6 +53,7 @@ const CardTitle: React.FC<CardTitleProps> = props => {
children, children,
height, height,
title, title,
subtitle,
toolbar, toolbar,
onClick, onClick,
...rest ...rest

View file

@ -1,10 +1,10 @@
import { Divider } from "@material-ui/core"; import { Divider } from "@material-ui/core";
import initial from "lodash/initial";
import React from "react"; import React from "react";
interface CollectionWithDividersProps<T> { interface CollectionWithDividersProps<T> {
DividerComponent?: React.FunctionComponent; DividerComponent?: React.FunctionComponent;
renderEmpty?: (collection: T[]) => any; renderEmpty?: (collection: T[]) => any;
withOuterDividers?: boolean;
collection: T[]; collection: T[];
renderItem: ( renderItem: (
item: T | undefined, item: T | undefined,
@ -13,7 +13,25 @@ interface CollectionWithDividersProps<T> {
) => any; ) => any;
} }
const Wrapper: React.FC<{
withOuterDividers?: boolean;
SelectedDivider?: React.FunctionComponent;
}> = ({ withOuterDividers, SelectedDivider, children }) => (
<div>
{withOuterDividers && SelectedDivider ? (
<>
<SelectedDivider />
{children}
<SelectedDivider />
</>
) : (
<>{children}</>
)}
</div>
);
function CollectionWithDividers<T>({ function CollectionWithDividers<T>({
withOuterDividers = false,
collection, collection,
renderItem, renderItem,
DividerComponent, DividerComponent,
@ -31,15 +49,20 @@ function CollectionWithDividers<T>({
const SelectedDividerComponent = DividerComponent || Divider; const SelectedDividerComponent = DividerComponent || Divider;
return initial( return (
collection.reduce( <Wrapper
(result, item, index) => [ withOuterDividers={withOuterDividers}
...result, SelectedDivider={SelectedDividerComponent}
renderItem(item, index, collection), >
<SelectedDividerComponent /> <>
], {collection.map((item, index) => (
[] <>
) {renderItem(item, index, collection)}
<SelectedDividerComponent />
</>
))}
</>
</Wrapper>
); );
} }

View file

@ -1,7 +1,7 @@
import { Checkbox, FormControlLabel } from "@material-ui/core"; import { Checkbox, FormControlLabel } from "@material-ui/core";
import React from "react"; import React from "react";
interface ControlledCheckboxProps { export interface ControlledCheckboxProps {
className?: string; className?: string;
name: string; name: string;
label?: React.ReactNode; label?: React.ReactNode;

View file

@ -54,6 +54,7 @@ const useStyles = makeStyles(
interface ExtendedPageHeaderProps { interface ExtendedPageHeaderProps {
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
childrenWrapperClassName?: string;
inline?: boolean; inline?: boolean;
underline?: boolean; underline?: boolean;
title?: React.ReactNode; title?: React.ReactNode;
@ -61,7 +62,15 @@ interface ExtendedPageHeaderProps {
} }
const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => { const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
const { children, className, inline, underline, title, testId } = props; const {
children,
className,
childrenWrapperClassName,
inline,
underline,
title,
testId
} = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -75,7 +84,9 @@ const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
})} })}
> >
{title} {title}
<div className={classes.action}>{children}</div> <div className={classNames(classes.action, childrenWrapperClassName)}>
{children}
</div>
</div> </div>
{underline && ( {underline && (
<div className={classes.underline}> <div className={classes.underline}>

View file

@ -1,13 +1,10 @@
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer"; import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import StatusChip from "@saleor/components/StatusChip";
import { StatusType } from "@saleor/components/StatusChip/types";
import { makeStyles } from "@saleor/macaw-ui"; import { makeStyles } from "@saleor/macaw-ui";
import React from "react"; import React from "react";
import ExtendedPageHeader from "../ExtendedPageHeader";
export interface PageTitleWithStatusChipProps { export interface PageTitleWithStatusChipProps {
title: string; title: string;
statusLabel: string;
statusType: StatusType;
} }
const useStyles = makeStyles( const useStyles = makeStyles(
@ -22,17 +19,20 @@ const useStyles = makeStyles(
const PageTitleWithStatusChip: React.FC<PageTitleWithStatusChipProps> = ({ const PageTitleWithStatusChip: React.FC<PageTitleWithStatusChipProps> = ({
title, title,
statusLabel, children
statusType
}) => { }) => {
const classes = useStyles({}); const classes = useStyles({});
return ( return (
<div className={classes.container}> <ExtendedPageHeader
{title} title={title}
<HorizontalSpacer spacing={2} /> childrenWrapperClassName={classes.container}
<StatusChip label={statusLabel} status={statusType} /> >
</div> <>
<HorizontalSpacer spacing={2} />
{children}
</>
</ExtendedPageHeader>
); );
}; };

View file

@ -31,7 +31,13 @@ const Story: React.FC<Partial<
SingleAutocompleteSelectFieldProps & { SingleAutocompleteSelectFieldProps & {
enableLoadMore: boolean; enableLoadMore: boolean;
} }
>> = ({ allowCustomValues, emptyOption, enableLoadMore, nakedInput }) => { >> = ({
allowCustomValues,
emptyOption,
enableLoadMore,
nakedInput,
disabled
}) => {
const [displayValue, setDisplayValue] = React.useState(suggestions[0].label); const [displayValue, setDisplayValue] = React.useState(suggestions[0].label);
return ( return (
@ -60,6 +66,7 @@ const Story: React.FC<Partial<
allowCustomValues={allowCustomValues} allowCustomValues={allowCustomValues}
emptyOption={emptyOption} emptyOption={emptyOption}
nakedInput={nakedInput} nakedInput={nakedInput}
disabled={disabled}
/> />
); );
}} }}
@ -106,9 +113,13 @@ storiesOf("Generics / Select with autocomplete", module)
<SingleAutocompleteSelectFieldContent {...contentProps} choices={[]} /> <SingleAutocompleteSelectFieldContent {...contentProps} choices={[]} />
)) ))
.add("naked", () => <Story nakedInput />) .add("naked", () => <Story nakedInput />)
.add("naked and disabled", () => <Story nakedInput disabled />)
.add("interactive", () => <Story />) .add("interactive", () => <Story />)
.add("interactive with custom option", () => ( .add("interactive with custom option", () => (
<Story allowCustomValues={true} /> <Story allowCustomValues={true} />
)) ))
.add("interactive with empty option", () => <Story emptyOption={true} />) .add("interactive with empty option", () => <Story emptyOption={true} />)
.add("interactive with load more", () => <Story enableLoadMore={true} />); .add("interactive with load more", () => <Story enableLoadMore={true} />)
.add("disabled", () => (
<Story enableLoadMore={true} {...contentProps} disabled />
));

View file

@ -187,7 +187,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
error, error,
id: undefined, id: undefined,
onBlur: handleBlur, onBlur: handleBlur,
onClick: toggleMenu, onClick: !disabled && toggleMenu,
onFocus: () => { onFocus: () => {
if (fetchOnFocus) { if (fetchOnFocus) {
fetchChoices(inputValue); fetchChoices(inputValue);

View file

@ -13,6 +13,8 @@ import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { singleSelectFieldItemHeight } from "./consts";
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
formControl: { formControl: {
@ -26,6 +28,9 @@ const useStyles = makeStyles(
}, },
noLabel: { noLabel: {
padding: theme.spacing(2, 1.5) padding: theme.spacing(2, 1.5)
},
paper: {
maxHeight: `calc(${singleSelectFieldItemHeight}px * 10 + ${singleSelectFieldItemHeight}px * 0.5)`
} }
}), }),
{ name: "SingleSelectField" } { name: "SingleSelectField" }
@ -111,6 +116,11 @@ export const SingleSelectField: React.FC<SingleSelectFieldProps> = props => {
/> />
} }
{...selectProps} {...selectProps}
MenuProps={{
classes: {
paper: classes.paper
}
}}
> >
{choices.length > 0 ? ( {choices.length > 0 ? (
choices.map(choice => ( choices.map(choice => (

View file

@ -0,0 +1 @@
export const singleSelectFieldItemHeight = "36";

View file

@ -25,7 +25,8 @@ const useStyles = makeStyles(
}, },
label: { label: {
alignSelf: "center", alignSelf: "center",
display: "inline-block" display: "inline-block",
userSelect: "none"
}, },
labelContainer: { labelContainer: {
"&:hover": { "&:hover": {
@ -45,6 +46,9 @@ const useStyles = makeStyles(
}, },
root: { root: {
cursor: "pointer" cursor: "pointer"
},
notSortable: {
cursor: "unset"
} }
}), }),
{ name: "TableCellHeader" } { name: "TableCellHeader" }
@ -69,6 +73,7 @@ const TableCellHeader: React.FC<TableCellHeaderProps> = props => {
textAlign, textAlign,
disabled = false, disabled = false,
onClick, onClick,
title,
...rest ...rest
} = props; } = props;
@ -76,12 +81,15 @@ const TableCellHeader: React.FC<TableCellHeaderProps> = props => {
<TableCell <TableCell
{...rest} {...rest}
onClick={e => { onClick={e => {
if (!disabled) { if (disabled || !onClick) {
e.preventDefault();
} else {
onClick(e); onClick(e);
} }
}} }}
className={classNames(classes.root, className, { className={classNames(classes.root, className, {
[classes.disabled]: disabled [classes.disabled]: disabled,
[classes.notSortable]: !onClick
})} })}
> >
<div <div

View file

@ -65,6 +65,7 @@ interface TimelineProps {
} }
interface TimelineAddNoteProps { interface TimelineAddNoteProps {
disabled?: boolean;
message: string; message: string;
reset: () => void; reset: () => void;
onChange(event: React.ChangeEvent<any>); onChange(event: React.ChangeEvent<any>);
@ -80,7 +81,7 @@ export const Timeline: React.FC<TimelineProps> = props => {
}; };
export const TimelineAddNote: React.FC<TimelineAddNoteProps> = props => { export const TimelineAddNote: React.FC<TimelineAddNoteProps> = props => {
const { message, onChange, onSubmit, reset } = props; const { message, onChange, onSubmit, reset, disabled } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
@ -100,6 +101,7 @@ export const TimelineAddNote: React.FC<TimelineAddNoteProps> = props => {
<PersonIcon /> <PersonIcon />
</Avatar> </Avatar>
<TextField <TextField
disabled={disabled}
className={classes.input} className={classes.input}
placeholder={intl.formatMessage({ placeholder={intl.formatMessage({
defaultMessage: "Leave your note here..." defaultMessage: "Leave your note here..."
@ -115,6 +117,7 @@ export const TimelineAddNote: React.FC<TimelineAddNoteProps> = props => {
className={classes.button} className={classes.button}
color="primary" color="primary"
onClick={e => submit(e)} onClick={e => submit(e)}
disabled={disabled}
> >
<FormattedMessage <FormattedMessage
defaultMessage="Send" defaultMessage="Send"

View file

@ -80,7 +80,7 @@ export interface TimelineEventProps {
children?: React.ReactNode; children?: React.ReactNode;
date: string; date: string;
secondaryTitle?: string; secondaryTitle?: string;
title?: string; title?: React.ReactNode;
titleElements?: TitleElement[]; titleElements?: TitleElement[];
} }

View file

@ -43,7 +43,7 @@ export interface TitleElement {
} }
export interface TimelineEventHeaderProps { export interface TimelineEventHeaderProps {
title?: string; title?: React.ReactNode;
date: string; date: string;
titleElements?: TitleElement[]; titleElements?: TitleElement[];
secondaryTitle?: string; secondaryTitle?: string;

View file

@ -66,6 +66,8 @@ interface TimelineNoteProps {
message: string | null; message: string | null;
user: { user: {
email: string; email: string;
firstName?: string;
lastName?: string;
}; };
} }
@ -74,6 +76,14 @@ export const TimelineNote: React.FC<TimelineNoteProps> = props => {
const classes = useStyles(props); const classes = useStyles(props);
const getUserTitleOrEmail = () => {
if (user?.firstName && user?.lastName) {
return `${user.firstName} ${user.lastName}`;
}
return user?.email;
};
return ( return (
<div className={classes.root}> <div className={classes.root}>
{user && ( {user && (
@ -85,7 +95,7 @@ export const TimelineNote: React.FC<TimelineNoteProps> = props => {
</Avatar> </Avatar>
)} )}
<div className={classes.title}> <div className={classes.title}>
<Typography>{user?.email}</Typography> <Typography>{getUserTitleOrEmail()}</Typography>
<Typography> <Typography>
<DateTime date={date} /> <DateTime date={date} />
</Typography> </Typography>

View file

@ -8,6 +8,7 @@ import { MetadataFormData } from "@saleor/components/Metadata/types";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import Savebar from "@saleor/components/Savebar"; import Savebar from "@saleor/components/Savebar";
import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment"; import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment";
import CustomerGiftCardsCard from "@saleor/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard";
import { SubmitPromise } from "@saleor/hooks/useForm"; import { SubmitPromise } from "@saleor/hooks/useForm";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { Backlink } from "@saleor/macaw-ui"; import { Backlink } from "@saleor/macaw-ui";
@ -117,6 +118,8 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
/> />
<CardSpacer /> <CardSpacer />
<CustomerStats customer={customer} /> <CustomerStats customer={customer} />
<CardSpacer />
<CustomerGiftCardsCard />
</div> </div>
</Grid> </Grid>
<Savebar <Savebar

View file

@ -0,0 +1,8 @@
import { CustomerDetailsContext } from "@saleor/customers/providers/CustomerDetailsProvider";
import { useContext } from "react";
export const useCustomerDetails = () => {
const customerDetails = useContext(CustomerDetailsContext);
return customerDetails;
};

View file

@ -0,0 +1,40 @@
import React, { createContext } from "react";
import { useCustomerDetails } from "../queries";
import { CustomerDetails } from "../types/CustomerDetails";
export interface CustomerDetailsProviderProps {
id: string;
}
interface CustomerDetailsConsumerProps {
customer: CustomerDetails | null;
loading: boolean | null;
}
export const CustomerDetailsContext = createContext<
CustomerDetailsConsumerProps
>(null);
export const CustomerDetailsProvider: React.FC<CustomerDetailsProviderProps> = ({
children,
id
}) => {
const { data, loading } = useCustomerDetails({
displayLoader: true,
variables: {
id
}
});
const providerValues: CustomerDetailsConsumerProps = {
customer: data,
loading
};
return (
<CustomerDetailsContext.Provider value={providerValues}>
{children}
</CustomerDetailsContext.Provider>
);
};

View file

@ -90,7 +90,8 @@ const customerDetails = gql`
} }
} }
`; `;
export const TypedCustomerDetailsQuery = TypedQuery<
export const useCustomerDetails = makeQuery<
CustomerDetails, CustomerDetails,
CustomerDetailsVariables CustomerDetailsVariables
>(customerDetails); >(customerDetails);

View file

@ -1,36 +1,18 @@
import { DialogContentText } from "@material-ui/core";
import ActionDialog from "@saleor/components/ActionDialog";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { getStringOrPlaceholder } from "@saleor/misc";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import {
useMetadataUpdate,
usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { orderListUrl, orderUrl } from "../../orders/urls";
import CustomerDetailsPage, {
CustomerDetailsPageFormData
} from "../components/CustomerDetailsPage/CustomerDetailsPage";
import { import {
TypedRemoveCustomerMutation, TypedRemoveCustomerMutation,
TypedUpdateCustomerMutation TypedUpdateCustomerMutation
} from "../mutations"; } from "../mutations";
import { TypedCustomerDetailsQuery } from "../queries"; import { CustomerDetailsProvider } from "../providers/CustomerDetailsProvider";
import { RemoveCustomer } from "../types/RemoveCustomer"; import { RemoveCustomer } from "../types/RemoveCustomer";
import { UpdateCustomer } from "../types/UpdateCustomer"; import { UpdateCustomer } from "../types/UpdateCustomer";
import { import { customerListUrl, CustomerUrlQueryParams } from "../urls";
customerAddressesUrl, import { CustomerDetailsContent } from "./CustomerDetailsContent";
customerListUrl,
customerUrl,
CustomerUrlQueryParams
} from "../urls";
interface CustomerDetailsViewProps { interface CustomerDetailsViewProps {
id: string; id: string;
@ -75,109 +57,18 @@ export const CustomerDetailsView: React.FC<CustomerDetailsViewProps> = ({
{(removeCustomer, removeCustomerOpts) => ( {(removeCustomer, removeCustomerOpts) => (
<TypedUpdateCustomerMutation onCompleted={handleCustomerUpdateSuccess}> <TypedUpdateCustomerMutation onCompleted={handleCustomerUpdateSuccess}>
{(updateCustomer, updateCustomerOpts) => ( {(updateCustomer, updateCustomerOpts) => (
<TypedCustomerDetailsQuery displayLoader variables={{ id }}> <CustomerDetailsProvider id={id}>
{customerDetails => { <CustomerDetailsContent
const user = customerDetails.data?.user; handleBack={handleBack}
id={id}
if (user === null) { navigate={navigate}
return <NotFoundPage onBack={handleBack} />; params={params}
} removeCustomer={removeCustomer}
removeCustomerOpts={removeCustomerOpts}
const [updateMetadata] = useMetadataUpdate({}); updateCustomer={updateCustomer}
const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); updateCustomerOpts={updateCustomerOpts}
/>
const updateData = async ( </CustomerDetailsProvider>
data: CustomerDetailsPageFormData
) => {
const result = await updateCustomer({
variables: {
id,
input: {
email: data.email,
firstName: data.firstName,
isActive: data.isActive,
lastName: data.lastName,
note: data.note
}
}
});
return result.data.customerUpdate.errors;
};
const handleSubmit = createMetadataUpdateHandler(
user,
updateData,
variables => updateMetadata({ variables }),
variables => updatePrivateMetadata({ variables })
);
return (
<>
<WindowTitle title={user?.email} />
<CustomerDetailsPage
customer={user}
disabled={
customerDetails.loading ||
updateCustomerOpts.loading ||
removeCustomerOpts.loading
}
errors={
updateCustomerOpts.data?.customerUpdate.errors || []
}
saveButtonBar={updateCustomerOpts.status}
onAddressManageClick={() =>
navigate(customerAddressesUrl(id))
}
onBack={handleBack}
onRowClick={id => navigate(orderUrl(id))}
onSubmit={handleSubmit}
onDelete={() =>
navigate(
customerUrl(id, {
action: "remove"
})
)
}
onViewAllOrdersClick={() =>
navigate(
orderListUrl({
customer: user?.email
})
)
}
/>
<ActionDialog
confirmButtonState={removeCustomerOpts.status}
onClose={() =>
navigate(customerUrl(id), { replace: true })
}
onConfirm={() => removeCustomer()}
title={intl.formatMessage({
defaultMessage: "Delete Customer",
description: "dialog header"
})}
variant="delete"
open={params.action === "remove"}
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {email}?"
description="delete customer, dialog content"
values={{
email: (
<strong>
{getStringOrPlaceholder(user?.email)}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
}}
</TypedCustomerDetailsQuery>
)} )}
</TypedUpdateCustomerMutation> </TypedUpdateCustomerMutation>
)} )}

View file

@ -0,0 +1,150 @@
import { DialogContentText } from "@material-ui/core";
import ActionDialog from "@saleor/components/ActionDialog";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { UseNavigatorResult } from "@saleor/hooks/useNavigator";
import { getStringOrPlaceholder } from "@saleor/misc";
import { MutationResultAdditionalProps } from "@saleor/types";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
import {
useMetadataUpdate,
usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata";
import React, { useContext } from "react";
import { MutationFunction, MutationResult } from "react-apollo";
import { FormattedMessage, useIntl } from "react-intl";
import { orderListUrl, orderUrl } from "../../orders/urls";
import CustomerDetailsPage, {
CustomerDetailsPageFormData
} from "../components/CustomerDetailsPage/CustomerDetailsPage";
import { CustomerDetailsContext } from "../providers/CustomerDetailsProvider";
import {
RemoveCustomer,
RemoveCustomerVariables
} from "../types/RemoveCustomer";
import {
UpdateCustomer,
UpdateCustomerVariables
} from "../types/UpdateCustomer";
import {
customerAddressesUrl,
customerUrl,
CustomerUrlQueryParams
} from "../urls";
export interface CustomerDetailsContentProps {
handleBack: () => void;
updateCustomer: MutationFunction<UpdateCustomer, UpdateCustomerVariables>;
removeCustomer: MutationFunction<RemoveCustomer, RemoveCustomerVariables>;
id: string;
updateCustomerOpts: MutationResult<UpdateCustomer> &
MutationResultAdditionalProps;
removeCustomerOpts: MutationResult<RemoveCustomer> &
MutationResultAdditionalProps;
navigate: UseNavigatorResult;
params: CustomerUrlQueryParams;
}
export const CustomerDetailsContent: React.FC<CustomerDetailsContentProps> = ({
handleBack,
updateCustomer,
id,
updateCustomerOpts,
removeCustomerOpts,
navigate,
removeCustomer,
params
}) => {
const customerDetails = useContext(CustomerDetailsContext);
const user = customerDetails?.customer?.user;
const customerDetailsLoading = customerDetails?.loading;
const intl = useIntl();
if (user === null) {
return <NotFoundPage onBack={handleBack} />;
}
const [updateMetadata] = useMetadataUpdate({});
const [updatePrivateMetadata] = usePrivateMetadataUpdate({});
const updateData = async (data: CustomerDetailsPageFormData) => {
const result = await updateCustomer({
variables: {
id,
input: {
email: data.email,
firstName: data.firstName,
isActive: data.isActive,
lastName: data.lastName,
note: data.note
}
}
});
return result.data.customerUpdate.errors;
};
const handleSubmit = createMetadataUpdateHandler(
user,
updateData,
variables => updateMetadata({ variables }),
variables => updatePrivateMetadata({ variables })
);
return (
<>
<WindowTitle title={user?.email} />
<CustomerDetailsPage
customer={user}
disabled={
customerDetailsLoading ||
updateCustomerOpts.loading ||
removeCustomerOpts.loading
}
errors={updateCustomerOpts.data?.customerUpdate.errors || []}
saveButtonBar={updateCustomerOpts.status}
onAddressManageClick={() => navigate(customerAddressesUrl(id))}
onBack={handleBack}
onRowClick={id => navigate(orderUrl(id))}
onSubmit={handleSubmit}
onDelete={() =>
navigate(
customerUrl(id, {
action: "remove"
})
)
}
onViewAllOrdersClick={() =>
navigate(
orderListUrl({
customer: user?.email
})
)
}
/>
<ActionDialog
confirmButtonState={removeCustomerOpts.status}
onClose={() => navigate(customerUrl(id), { replace: true })}
onConfirm={() => removeCustomer()}
title={intl.formatMessage({
defaultMessage: "Delete Customer",
description: "dialog header"
})}
variant="delete"
open={params.action === "remove"}
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {email}?"
description="delete customer, dialog content"
values={{
email: <strong>{getStringOrPlaceholder(user?.email)}</strong>
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
};

View file

@ -0,0 +1,162 @@
import { Dialog, DialogTitle } from "@material-ui/core";
import { IMessage } from "@saleor/components/messages";
import useCurrentDate from "@saleor/hooks/useCurrentDate";
import useNotifier from "@saleor/hooks/useNotifier";
import { DialogProps } from "@saleor/types";
import { GiftCardBulkCreateInput } from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors";
import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl";
import ContentWithProgress from "../GiftCardCreateDialog/ContentWithProgress";
import GiftCardBulkCreateSuccessDialog from "../GiftCardCreateDialog/GiftCardBulkCreateSuccessDialog";
import { useChannelCurrencies } from "../GiftCardCreateDialog/queries";
import {
getGiftCardCreateOnCompletedMessage,
getGiftCardExpiryInputData
} from "../GiftCardCreateDialog/utils";
import { GIFT_CARD_LIST_QUERY } from "../GiftCardsList/types";
import GiftCardBulkCreateDialogForm from "./GiftCardBulkCreateDialogForm";
import { giftCardBulkCreateDialogMessages as messages } from "./messages";
import { useGiftCardBulkCreateMutation } from "./mutations";
import {
giftCardBulkCreateErrorKeys,
GiftCardBulkCreateFormData,
GiftCardBulkCreateFormErrors
} from "./types";
import { GiftCardBulkCreate } from "./types/GiftCardBulkCreate";
import { validateForm } from "./utils";
const GiftCardBulkCreateDialog: React.FC<DialogProps> = ({ onClose, open }) => {
const intl = useIntl();
const notify = useNotifier();
const [formErrors, setFormErrors] = useState<GiftCardBulkCreateFormErrors>(
null
);
const [issuedIds, setIssuedIds] = useState<string[] | null>(null);
const [openIssueSuccessDialog, setOpenIssueSuccessDialog] = useState<boolean>(
false
);
const onIssueSuccessDialogClose = () => setOpenIssueSuccessDialog(false);
const { loading: loadingChannelCurrencies } = useChannelCurrencies({});
const onCompleted = (data: GiftCardBulkCreate) => {
const errors = data?.giftCardBulkCreate?.errors;
const cardsAmount = data?.giftCardBulkCreate?.giftCards?.length || 0;
const giftCardsBulkIssueSuccessMessage: IMessage = {
status: "success",
title: intl.formatMessage(messages.createdSuccessAlertTitle),
text: intl.formatMessage(messages.createdSuccessAlertDescription, {
cardsAmount
})
};
notify(
getGiftCardCreateOnCompletedMessage(
errors,
intl,
giftCardsBulkIssueSuccessMessage
)
);
setFormErrors(getFormErrors(giftCardBulkCreateErrorKeys, errors));
if (!errors.length) {
setIssuedIds(
data?.giftCardBulkCreate?.giftCards?.map(giftCard => giftCard.id)
);
setOpenIssueSuccessDialog(true);
onClose();
}
};
const currentDate = useCurrentDate();
const getParsedSubmitInputData = (
formData: GiftCardBulkCreateFormData
): GiftCardBulkCreateInput => {
const {
balanceAmount,
balanceCurrency,
tags = [],
requiresActivation,
cardsAmount
} = formData;
return {
count: cardsAmount,
tags,
balance: {
amount: balanceAmount,
currency: balanceCurrency
},
expiryDate: getGiftCardExpiryInputData(formData, currentDate),
isActive: !requiresActivation
};
};
const [
bulkCreateGiftCard,
bulkCreateGiftCardOpts
] = useGiftCardBulkCreateMutation({
onCompleted,
refetchQueries: [GIFT_CARD_LIST_QUERY]
});
const handleSubmit = (data: GiftCardBulkCreateFormData) => {
const formErrors = validateForm(data);
if (!!Object.keys(formErrors).length) {
setFormErrors(formErrors);
} else {
bulkCreateGiftCard({
variables: {
input: getParsedSubmitInputData(data)
}
});
}
};
const apiErrors = bulkCreateGiftCardOpts?.data?.giftCardBulkCreate?.errors;
const handleSetSchemaErrors = () => {
if (apiErrors?.length) {
const formErrorsFromApi = getFormErrors(
giftCardBulkCreateErrorKeys,
apiErrors
);
setFormErrors(formErrorsFromApi);
}
};
useEffect(handleSetSchemaErrors, [apiErrors]);
return (
<>
<Dialog open={open} maxWidth="sm">
<DialogTitle>{intl.formatMessage(messages.title)}</DialogTitle>
<ContentWithProgress>
{!loadingChannelCurrencies && (
<GiftCardBulkCreateDialogForm
opts={bulkCreateGiftCardOpts}
onClose={onClose}
formErrors={formErrors}
onSubmit={handleSubmit}
/>
)}
</ContentWithProgress>
</Dialog>
<GiftCardBulkCreateSuccessDialog
onClose={onIssueSuccessDialogClose}
open={openIssueSuccessDialog}
idsToExport={issuedIds}
/>
</>
);
};
export default GiftCardBulkCreateDialog;

View file

@ -0,0 +1,151 @@
import {
DialogContent,
Divider,
TextField,
Typography
} from "@material-ui/core";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import DialogButtons from "@saleor/components/ActionDialog/DialogButtons";
import CardSpacer from "@saleor/components/CardSpacer";
import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput";
import useForm from "@saleor/hooks/useForm";
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
import {
GiftCardSettingsExpiryTypeEnum,
TimePeriodTypeEnum
} from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import GiftCardCreateExpirySelect from "../GiftCardCreateDialog/GiftCardCreateExpirySelect";
import GiftCardCreateMoneyInput from "../GiftCardCreateDialog/GiftCardCreateMoneyInput";
import GiftCardCreateRequiresActivationSection from "../GiftCardCreateDialog/GiftCardCreateRequiresActivationSection";
import { giftCardCreateMessages as messages } from "../GiftCardCreateDialog/messages";
import { useGiftCardCreateFormStyles as useStyles } from "../GiftCardCreateDialog/styles";
import { useGiftCardSettingsQuery } from "../GiftCardSettings/queries";
import { getGiftCardErrorMessage } from "../GiftCardUpdate/messages";
import {
GiftCardBulkCreateFormCommonProps,
GiftCardBulkCreateFormData,
GiftCardBulkCreateFormErrors
} from "./types";
export const initialData: GiftCardBulkCreateFormData = {
tags: [],
balanceAmount: 1,
balanceCurrency: null,
expirySelected: false,
expiryType: "EXPIRY_PERIOD",
expiryDate: "",
expiryPeriodType: TimePeriodTypeEnum.MONTH,
expiryPeriodAmount: 12,
requiresActivation: true,
cardsAmount: 100
};
interface GiftCardBulkCreateDialogFormProps {
opts: { status: ConfirmButtonTransitionState };
formErrors: GiftCardBulkCreateFormErrors;
onSubmit: (data: GiftCardBulkCreateFormData) => void;
onClose: () => void;
}
const GiftCardBulkCreateDialogForm: React.FC<GiftCardBulkCreateDialogFormProps> = ({
onSubmit,
opts,
onClose,
formErrors = {}
}) => {
const intl = useIntl();
const classes = useStyles({});
const {
data: settingsData,
loading: loadingSettings
} = useGiftCardSettingsQuery();
const getInitialExpirySettingsData = (): Partial<GiftCardBulkCreateFormData> => {
if (loadingSettings) {
return {};
}
const { expiryType, expiryPeriod } = settingsData?.giftCardSettings;
if (expiryType === GiftCardSettingsExpiryTypeEnum.NEVER_EXPIRE) {
return {};
}
return {
expiryType,
expiryPeriodType: expiryPeriod?.type,
expiryPeriodAmount: expiryPeriod?.amount
};
};
const { submit, toggleValue, change, data, set } = useForm(
{
...initialData,
...getInitialExpirySettingsData(),
balanceCurrency: ""
},
onSubmit
);
const { tags, requiresActivation, cardsAmount } = data;
const commonFormProps: GiftCardBulkCreateFormCommonProps = {
data,
errors: formErrors,
toggleValue,
change
};
return (
<>
<DialogContent className={classes.dialogContent}>
<TextField
error={!!formErrors?.count}
name="cardsAmount"
onChange={change}
className={classes.fullWidthContainer}
label={intl.formatMessage(messages.giftCardsAmountLabel)}
value={cardsAmount}
helperText={getGiftCardErrorMessage(formErrors?.count, intl)}
/>
<VerticalSpacer spacing={2} />
<GiftCardCreateMoneyInput {...commonFormProps} set={set} />
<VerticalSpacer spacing={2} />
<GiftCardTagInput
optional={false}
error={formErrors?.tags}
name="tags"
values={tags}
toggleChange={toggleValue}
/>
<CardSpacer />
<Divider />
<VerticalSpacer />
<GiftCardCreateExpirySelect {...commonFormProps} />
<VerticalSpacer />
<Divider />
<VerticalSpacer spacing={2} />
<GiftCardCreateRequiresActivationSection
onChange={change}
checked={requiresActivation}
/>
<VerticalSpacer spacing={2} />
<Typography>
{intl.formatMessage(messages.bulkCreateExplanation)}
</Typography>
</DialogContent>
<DialogButtons
onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.issueButtonLabel)}
confirmButtonState={opts?.status}
onClose={onClose}
/>
</>
);
};
export default GiftCardBulkCreateDialogForm;

View file

@ -0,0 +1,2 @@
export * from "./GiftCardBulkCreateDialog";
export { default } from "./GiftCardBulkCreateDialog";

View file

@ -0,0 +1,16 @@
import { defineMessages } from "react-intl";
export const giftCardBulkCreateDialogMessages = defineMessages({
title: {
defaultMessage: "Bulk Issue Gift Cards",
description: "bulk issue gift cards dialog title"
},
createdSuccessAlertTitle: {
defaultMessage: "Gift Cards Issued",
description: "bulk issue gift cards success alert title"
},
createdSuccessAlertDescription: {
defaultMessage: "Requested {cardsAmount} were successfully issued",
description: "bulk issue gift cards success alert description"
}
});

View file

@ -0,0 +1,26 @@
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import {
GiftCardBulkCreate,
GiftCardBulkCreateVariables
} from "./types/GiftCardBulkCreate";
const giftCardBulkCreate = gql`
mutation GiftCardBulkCreate($input: GiftCardBulkCreateInput!) {
giftCardBulkCreate(input: $input) {
giftCards {
id
}
errors {
code
field
}
}
}
`;
export const useGiftCardBulkCreateMutation = makeMutation<
GiftCardBulkCreate,
GiftCardBulkCreateVariables
>(giftCardBulkCreate);

View file

@ -0,0 +1,58 @@
import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { FormChange } from "@saleor/hooks/useForm";
import { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
import { GiftCardExpiryType } from "../GiftCardCreateDialog/types";
export type GiftCardErrorKey =
| "tags"
| "expiryDate"
| "currency"
| "expiryPeriod"
| "amount"
| "balance"
| "count";
export const giftCardBulkCreateErrorKeys: GiftCardErrorKey[] = [
"tags",
"expiryDate",
"currency",
"amount",
"balance",
"count"
];
export interface GiftCardBulkCreateFormData
extends GiftCardCreateCommonFormData {
cardsAmount: number;
}
export type GiftCardBulkCreateFormError = Pick<GiftCardError, "code" | "field">;
export type GiftCardBulkCreateFormErrors = Partial<
Record<GiftCardErrorKey, GiftCardBulkCreateFormError>
>;
export interface GiftCardBulkCreateFormCommonProps {
change: FormChange;
toggleValue: FormChange;
errors: GiftCardBulkCreateFormErrors;
data: GiftCardBulkCreateFormData;
}
export interface GiftCardCreateCommonFormData {
expirySelected: boolean;
expiryType: GiftCardExpiryType;
expiryPeriodType: TimePeriodTypeEnum;
expiryPeriodAmount: number;
requiresActivation: boolean;
tags: string[];
balanceAmount: number;
balanceCurrency: string;
expiryDate: string;
}
export interface GiftCardBulkCreateFormData
extends GiftCardCreateCommonFormData {
cardsAmount: number;
}

View file

@ -0,0 +1,35 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardBulkCreateInput, GiftCardErrorCode } from "./../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: GiftCardBulkCreate
// ====================================================
export interface GiftCardBulkCreate_giftCardBulkCreate_giftCards {
__typename: "GiftCard";
id: string;
}
export interface GiftCardBulkCreate_giftCardBulkCreate_errors {
__typename: "GiftCardError";
code: GiftCardErrorCode;
field: string | null;
}
export interface GiftCardBulkCreate_giftCardBulkCreate {
__typename: "GiftCardBulkCreate";
giftCards: GiftCardBulkCreate_giftCardBulkCreate_giftCards[];
errors: GiftCardBulkCreate_giftCardBulkCreate_errors[];
}
export interface GiftCardBulkCreate {
giftCardBulkCreate: GiftCardBulkCreate_giftCardBulkCreate | null;
}
export interface GiftCardBulkCreateVariables {
input: GiftCardBulkCreateInput;
}

View file

@ -0,0 +1,64 @@
import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { GiftCardErrorCode } from "@saleor/types/globalTypes";
import reduce from "lodash/reduce";
import {
GiftCardBulkCreateFormData,
GiftCardBulkCreateFormErrors
} from "./types";
export const validateField = (
{
expiryDate,
expiryPeriodAmount,
expiryType,
expirySelected,
expiryPeriodType
}: GiftCardBulkCreateFormData,
value,
key: keyof GiftCardBulkCreateFormData
): Pick<GiftCardError, "field" | "code"> | null => {
const error = { code: GiftCardErrorCode.INVALID, field: key };
const expiryDateSelected = expirySelected && expiryType === "EXPIRY_DATE";
const expiryPeriodSelected = expirySelected && expiryType === "EXPIRY_PERIOD";
switch (key) {
case "cardsAmount":
case "tags":
case "balanceCurrency":
case "balanceAmount":
return !value ? error : null;
case "expiryDate":
return expiryDateSelected && !expiryDate ? error : null;
case "expiryPeriodAmount":
return expiryPeriodSelected && (!expiryPeriodType || !expiryPeriodAmount)
? { ...error, field: "expiryDate" }
: null;
}
};
export const validateForm = (
formData: GiftCardBulkCreateFormData
): GiftCardBulkCreateFormErrors =>
reduce(
formData,
(resultErrors, value, key: keyof GiftCardBulkCreateFormData) => {
const correspondingKeys = {
cardsAmount: "count",
balanceCurrency: "balance",
balanceAmount: "balance",
expiryPeriodAmount: "expiryDate"
};
const formError = validateField(formData, value, key);
if (!formError) {
return resultErrors;
}
return { ...resultErrors, [correspondingKeys[key] || key]: formError };
},
{}
);

View file

@ -15,7 +15,7 @@ export const useStyles = makeStyles(
justifyContent: "center", justifyContent: "center",
height: "100%", height: "100%",
width: "100%", width: "100%",
padding: theme.spacing padding: theme.spacing(3)
} }
}), }),
{ name: "ContentWithProgress" } { name: "ContentWithProgress" }

View file

@ -0,0 +1,69 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Typography
} from "@material-ui/core";
import { DialogProps } from "@saleor/types";
import React from "react";
import { useState } from "react";
import { FormattedMessage } from "react-intl";
import { useIntl } from "react-intl";
import GiftCardExportDialogContent from "../GiftCardExportDialogContent";
import { giftCardCreateMessages as messages } from "./messages";
interface GiftCardBulkCreateSuccessDialogProps extends DialogProps {
idsToExport: string[] | null;
}
const GiftCardBulkCreateSuccessDialog: React.FC<GiftCardBulkCreateSuccessDialogProps> = ({
open,
onClose,
idsToExport
}) => {
const intl = useIntl();
const [openEmailExport, setOpenEmailExport] = useState(false);
const onExportDialogClose = () => {
setOpenEmailExport(false);
onClose();
};
return (
<>
<Dialog open={open} maxWidth="sm">
<DialogTitle>
{intl.formatMessage(messages.bulkCreateIssuedTitle)}
</DialogTitle>
<DialogContent>
<Typography>
{intl.formatMessage(messages.bulkCreateIssuedExplanation)}
</Typography>
</DialogContent>
<DialogActions>
<Button
variant="outlined"
color="secondary"
onClick={() => setOpenEmailExport(true)}
>
<FormattedMessage {...messages.bulkCreateIssuedExportToEmail} />
</Button>
<Button variant="contained" color="primary" onClick={onClose}>
<FormattedMessage {...messages.bulkCreateIssuedAccept} />
</Button>
</DialogActions>
</Dialog>
<Dialog open={openEmailExport} maxWidth="sm">
<GiftCardExportDialogContent
idsToExport={idsToExport}
onClose={onExportDialogClose}
/>
</Dialog>
</>
);
};
export default GiftCardBulkCreateSuccessDialog;

View file

@ -7,7 +7,7 @@ import { buttonMessages } from "@saleor/intl";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { giftCardCreateDialogMessages as messages } from "./messages"; import { giftCardCreateMessages as messages } from "./messages";
import { useGiftCardCreateDialogCodeContentStyles as useStyles } from "./styles"; import { useGiftCardCreateDialogCodeContentStyles as useStyles } from "./styles";
interface GiftCardCreateDialogCodeContentProps { interface GiftCardCreateDialogCodeContentProps {

View file

@ -1,28 +1,36 @@
import { Dialog, DialogTitle } from "@material-ui/core"; import { DialogTitle } from "@material-ui/core";
import { IMessage } from "@saleor/components/messages";
import useCurrentDate from "@saleor/hooks/useCurrentDate"; import useCurrentDate from "@saleor/hooks/useCurrentDate";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { DialogProps } from "@saleor/types";
import { GiftCardCreateInput } from "@saleor/types/globalTypes"; import { GiftCardCreateInput } from "@saleor/types/globalTypes";
import commonErrorMessages from "@saleor/utils/errors/common";
import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers";
import React, { useState } from "react"; import React, { useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { GIFT_CARD_LIST_QUERY } from "../GiftCardsList/types";
import ContentWithProgress from "./ContentWithProgress"; import ContentWithProgress from "./ContentWithProgress";
import GiftCardCreateDialogCodeContent from "./GiftCardCreateDialogCodeContent"; import GiftCardCreateDialogCodeContent from "./GiftCardCreateDialogCodeContent";
import GiftCardCreateDialogForm, { import GiftCardCreateDialogForm, {
GiftCardCreateFormData GiftCardCreateFormData
} from "./GiftCardCreateDialogForm"; } from "./GiftCardCreateDialogForm";
import { giftCardCreateDialogMessages as messages } from "./messages"; import { giftCardCreateMessages as messages } from "./messages";
import { useGiftCardCreateMutation } from "./mutations"; import { useGiftCardCreateMutation } from "./mutations";
import { useChannelCurrencies } from "./queries"; import { useChannelCurrencies } from "./queries";
import { GiftCardCreateFormCustomer } from "./types";
import { GiftCardCreate } from "./types/GiftCardCreate"; import { GiftCardCreate } from "./types/GiftCardCreate";
import { getGiftCardExpiryInputData } from "./utils"; import {
getGiftCardCreateOnCompletedMessage,
getGiftCardExpiryInputData
} from "./utils";
const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({ interface GiftCardCreateDialogContentProps
closeDialog, extends Pick<DialogProps, "onClose"> {
open refetchQueries: string[];
initialCustomer?: GiftCardCreateFormCustomer | null;
}
const GiftCardCreateDialogContent: React.FC<GiftCardCreateDialogContentProps> = ({
onClose,
refetchQueries,
initialCustomer
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const notify = useNotifier(); const notify = useNotifier();
@ -34,17 +42,7 @@ const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
const onCompleted = (data: GiftCardCreate) => { const onCompleted = (data: GiftCardCreate) => {
const errors = data?.giftCardCreate?.errors; const errors = data?.giftCardCreate?.errors;
const notifierData: IMessage = !!errors?.length notify(getGiftCardCreateOnCompletedMessage(errors, intl));
? {
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
}
: {
status: "success",
text: intl.formatMessage(messages.createdSuccessAlertTitle)
};
notify(notifierData);
if (!errors?.length) { if (!errors?.length) {
setCardCode(data?.giftCardCreate?.giftCard?.code); setCardCode(data?.giftCardCreate?.giftCard?.code);
@ -60,7 +58,7 @@ const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
balanceAmount, balanceAmount,
balanceCurrency, balanceCurrency,
note, note,
tag, tags,
sendToCustomerSelected, sendToCustomerSelected,
selectedCustomer, selectedCustomer,
requiresActivation, requiresActivation,
@ -69,7 +67,7 @@ const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
return { return {
note: note || null, note: note || null,
tag: tag || null, addTags: tags || null,
userEmail: (sendToCustomerSelected && selectedCustomer.email) || null, userEmail: (sendToCustomerSelected && selectedCustomer.email) || null,
channel: (sendToCustomerSelected && channelSlug) || null, channel: (sendToCustomerSelected && channelSlug) || null,
balance: { balance: {
@ -83,7 +81,7 @@ const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
const [createGiftCard, createGiftCardOpts] = useGiftCardCreateMutation({ const [createGiftCard, createGiftCardOpts] = useGiftCardCreateMutation({
onCompleted, onCompleted,
refetchQueries: [GIFT_CARD_LIST_QUERY] refetchQueries
}); });
const handleSubmit = (data: GiftCardCreateFormData) => { const handleSubmit = (data: GiftCardCreateFormData) => {
@ -95,14 +93,11 @@ const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
}; };
const handleClose = () => { const handleClose = () => {
closeDialog(); onClose();
// dialog closing animation runs slower than prop change
// and we don't want to show the form for a split second
setTimeout(() => setCardCode(null), 0);
}; };
return ( return (
<Dialog open={open} maxWidth="sm"> <>
<DialogTitle>{intl.formatMessage(messages.title)}</DialogTitle> <DialogTitle>{intl.formatMessage(messages.title)}</DialogTitle>
<ContentWithProgress> <ContentWithProgress>
{!loadingChannelCurrencies && {!loadingChannelCurrencies &&
@ -117,11 +112,12 @@ const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
onClose={handleClose} onClose={handleClose}
apiErrors={createGiftCardOpts?.data?.giftCardCreate?.errors} apiErrors={createGiftCardOpts?.data?.giftCardCreate?.errors}
onSubmit={handleSubmit} onSubmit={handleSubmit}
initialCustomer={initialCustomer}
/> />
))} ))}
</ContentWithProgress> </ContentWithProgress>
</Dialog> </>
); );
}; };
export default GiftCardCreateDialog; export default GiftCardCreateDialogContent;

View file

@ -1,13 +1,7 @@
import { import { DialogContent, Divider, TextField } from "@material-ui/core";
DialogContent,
Divider,
TextField,
Typography
} from "@material-ui/core";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer"; import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import DialogButtons from "@saleor/components/ActionDialog/DialogButtons"; import DialogButtons from "@saleor/components/ActionDialog/DialogButtons";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput"; import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput";
import useForm from "@saleor/hooks/useForm"; import useForm from "@saleor/hooks/useForm";
@ -20,40 +14,30 @@ import {
} from "@saleor/types/globalTypes"; } from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import React, { useState } from "react"; import React, { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { useIntl } from "react-intl";
import GiftCardSendToCustomer from "../components/GiftCardSendToCustomer/GiftCardSendToCustomer"; import GiftCardSendToCustomer from "../components/GiftCardSendToCustomer/GiftCardSendToCustomer";
import { GiftCardCreateCommonFormData } from "../GiftCardBulkCreateDialog/types";
import { useGiftCardSettingsQuery } from "../GiftCardSettings/queries"; import { useGiftCardSettingsQuery } from "../GiftCardSettings/queries";
import GiftCardCreateDialogMoneyInput from "./GiftCardCreateDialogMoneyInput";
import GiftCardCreateExpirySelect from "./GiftCardCreateExpirySelect"; import GiftCardCreateExpirySelect from "./GiftCardCreateExpirySelect";
import { giftCardCreateDialogMessages as messages } from "./messages"; import GiftCardCreateMoneyInput from "./GiftCardCreateMoneyInput";
import { useGiftCardCreateDialogFormStyles as useStyles } from "./styles"; import GiftCardCreateRequiresActivationSection from "./GiftCardCreateRequiresActivationSection";
import { giftCardCreateMessages as messages } from "./messages";
import { useGiftCardCreateFormStyles as useStyles } from "./styles";
import { import {
GiftCardCreateFormCommonProps, GiftCardCreateFormCommonProps,
GiftCardCreateFormCustomer, GiftCardCreateFormCustomer
GiftCardExpiryType
} from "./types"; } from "./types";
export interface GiftCardCreateFormData { export interface GiftCardCreateFormData extends GiftCardCreateCommonFormData {
note: string; note: string;
sendToCustomerSelected: boolean; sendToCustomerSelected: boolean;
selectedCustomer?: GiftCardCreateFormCustomer; selectedCustomer?: GiftCardCreateFormCustomer;
channelSlug?: string; channelSlug?: string;
expirySelected: boolean;
expiryType: GiftCardExpiryType;
expiryPeriodType: TimePeriodTypeEnum;
expiryPeriodAmount: number;
requiresActivation: boolean;
tag: string;
balanceAmount: number;
balanceCurrency: string;
expiryDate: string;
} }
const initialCustomer = { email: "", name: "" };
export const initialData: GiftCardCreateFormData = { export const initialData: GiftCardCreateFormData = {
tag: "", tags: [],
balanceAmount: 1, balanceAmount: 1,
balanceCurrency: null, balanceCurrency: null,
note: "", note: "",
@ -65,19 +49,22 @@ export const initialData: GiftCardCreateFormData = {
expiryPeriodAmount: 12, expiryPeriodAmount: 12,
requiresActivation: true requiresActivation: true
}; };
interface GiftCardCreateDialogFormProps { interface GiftCardCreateDialogFormProps {
opts: { status: ConfirmButtonTransitionState }; opts: { status: ConfirmButtonTransitionState };
apiErrors: GiftCardError[]; apiErrors: GiftCardError[];
onSubmit: (data: GiftCardCreateFormData) => void; onSubmit: (data: GiftCardCreateFormData) => void;
onClose: () => void; onClose: () => void;
initialCustomer?: GiftCardCreateFormCustomer | null;
} }
const defaultInitialCustomer = { email: "", name: "" };
const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
onSubmit, onSubmit,
opts, opts,
onClose, onClose,
apiErrors apiErrors,
initialCustomer
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
@ -89,7 +76,7 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
const [selectedCustomer, setSelectedCustomer] = useState< const [selectedCustomer, setSelectedCustomer] = useState<
GiftCardCreateFormCustomer GiftCardCreateFormCustomer
>(initialCustomer); >(initialCustomer || defaultInitialCustomer);
const handleSubmit = (data: GiftCardCreateFormData) => const handleSubmit = (data: GiftCardCreateFormData) =>
onSubmit({ ...data, selectedCustomer }); onSubmit({ ...data, selectedCustomer });
@ -112,23 +99,24 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
}; };
}; };
const { submit, change, data, set } = useForm( const { submit, change, toggleValue, data, set } = useForm(
{ {
...initialData, ...initialData,
...getInitialExpirySettingsData(), ...getInitialExpirySettingsData(),
balanceCurrency: "", balanceCurrency: "",
channelSlug: "" channelSlug: "",
sendToCustomerSelected: !!initialCustomer
}, },
handleSubmit handleSubmit
); );
const formErrors = getFormErrors( const formErrors = getFormErrors(
["tag", "expiryDate", "customer", "currency", "amount", "balance"], ["tags", "expiryDate", "customer", "currency", "amount", "balance"],
apiErrors apiErrors
); );
const { const {
tag, tags,
sendToCustomerSelected, sendToCustomerSelected,
channelSlug, channelSlug,
balanceAmount, balanceAmount,
@ -153,19 +141,25 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
const commonFormProps: GiftCardCreateFormCommonProps = { const commonFormProps: GiftCardCreateFormCommonProps = {
data, data,
errors: formErrors, errors: formErrors,
toggleValue,
change change
}; };
return ( return (
<> <>
<DialogContent> <DialogContent
<GiftCardCreateDialogMoneyInput {...commonFormProps} set={set} /> style={{
overflowY: "auto",
overflowX: "hidden"
}}
>
<GiftCardCreateMoneyInput {...commonFormProps} set={set} />
<CardSpacer /> <CardSpacer />
<GiftCardTagInput <GiftCardTagInput
error={formErrors?.tag} error={formErrors?.tags}
name="tag" name="tags"
value={tag} values={tags}
change={change} toggleChange={toggleValue}
/> />
<CardSpacer /> <CardSpacer />
<Divider /> <Divider />
@ -175,6 +169,7 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
sendToCustomerSelected={sendToCustomerSelected} sendToCustomerSelected={sendToCustomerSelected}
selectedCustomer={selectedCustomer} selectedCustomer={selectedCustomer}
setSelectedCustomer={setSelectedCustomer} setSelectedCustomer={setSelectedCustomer}
disabled={!!initialCustomer}
/> />
<Divider /> <Divider />
<VerticalSpacer /> <VerticalSpacer />
@ -192,18 +187,9 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
<VerticalSpacer /> <VerticalSpacer />
<Label text={intl.formatMessage(messages.noteSubtitle)} /> <Label text={intl.formatMessage(messages.noteSubtitle)} />
<VerticalSpacer spacing={2} /> <VerticalSpacer spacing={2} />
<ControlledCheckbox <GiftCardCreateRequiresActivationSection
name="requiresActivation"
label={
<>
<FormattedMessage {...messages.requiresActivationLabel} />
<Typography variant="caption">
<FormattedMessage {...messages.requiresActivationCaption} />
</Typography>
</>
}
checked={requiresActivation}
onChange={change} onChange={change}
checked={requiresActivation}
/> />
</DialogContent> </DialogContent>
<DialogButtons <DialogButtons

View file

@ -5,12 +5,14 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import RadioGroupField from "@saleor/components/RadioGroupField"; import RadioGroupField from "@saleor/components/RadioGroupField";
import TimePeriodField from "@saleor/giftCards/components/TimePeriodField"; import TimePeriodField from "@saleor/giftCards/components/TimePeriodField";
import { import {
GiftCardCreateFormCommonProps, GiftCardBulkCreateFormErrors,
GiftCardExpiryType GiftCardCreateCommonFormData
} from "@saleor/giftCards/GiftCardCreateDialog/types"; } from "@saleor/giftCards/GiftCardBulkCreateDialog/types";
import { GiftCardExpiryType } from "@saleor/giftCards/GiftCardCreateDialog/types";
import { getExpiryPeriodTerminationDate } from "@saleor/giftCards/GiftCardCreateDialog/utils"; import { getExpiryPeriodTerminationDate } from "@saleor/giftCards/GiftCardCreateDialog/utils";
import { getGiftCardErrorMessage } from "@saleor/giftCards/GiftCardUpdate/messages"; import { getGiftCardErrorMessage } from "@saleor/giftCards/GiftCardUpdate/messages";
import useCurrentDate from "@saleor/hooks/useCurrentDate"; import useCurrentDate from "@saleor/hooks/useCurrentDate";
import { FormChange } from "@saleor/hooks/useForm";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { MessageDescriptor, useIntl } from "react-intl"; import { MessageDescriptor, useIntl } from "react-intl";
@ -34,7 +36,20 @@ const options: UntranslatedOption[] = [
} }
]; ];
const GiftCardCreateExpirySelect: React.FC<GiftCardCreateFormCommonProps> = ({ interface GiftCardCreateExpirySelectProps {
errors: GiftCardBulkCreateFormErrors;
change: FormChange;
data: Pick<
GiftCardCreateCommonFormData,
| "expirySelected"
| "expiryPeriodType"
| "expiryPeriodAmount"
| "expiryType"
| "expiryDate"
>;
}
const GiftCardCreateExpirySelect: React.FC<GiftCardCreateExpirySelectProps> = ({
errors, errors,
change, change,
data: { data: {

View file

@ -3,18 +3,18 @@ import { defineMessages } from "react-intl";
export const giftCardCreateExpirySelectMessages = defineMessages({ export const giftCardCreateExpirySelectMessages = defineMessages({
expirySelectedLabel: { expirySelectedLabel: {
defaultMessage: "Set gift card expiry date", defaultMessage: "Set gift card expiry date",
description: "GiftCarUpdateDetailsExpirySection expiry selected label" description: "set expiry date selected label"
}, },
expiryPeriodLabel: { expiryPeriodLabel: {
defaultMessage: "Expires in", defaultMessage: "Expires in",
description: "GiftCarUpdateDetailsExpirySection expiry period label" description: "expires in label"
}, },
expiryDateLabel: { expiryDateLabel: {
defaultMessage: "Exact date", defaultMessage: "Exact date",
description: "GiftCarUpdateDetailsExpirySection expiry date label" description: "expiry date label"
}, },
expiryOnLabel: { expiryOnLabel: {
defaultMessage: "Will expire on:", defaultMessage: "Will expire on:",
description: "GiftCarUpdateDetailsExpirySection expiry on label" description: "expires on label"
} }
}); });

View file

@ -1,24 +1,28 @@
import TextWithSelectField from "@saleor/components/TextWithSelectField"; import TextWithSelectField from "@saleor/components/TextWithSelectField";
import { ChangeEvent } from "@saleor/hooks/useForm"; import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useLocalStorage from "@saleor/hooks/useLocalStorage";
import { mapSingleValueNodeToChoice } from "@saleor/utils/maps"; import { mapSingleValueNodeToChoice } from "@saleor/utils/maps";
import * as React from "react"; import * as React from "react";
import { useEffect } from "react"; import { useEffect } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import {
GiftCardBulkCreateFormErrors,
GiftCardCreateCommonFormData
} from "../GiftCardBulkCreateDialog/types";
import { getGiftCardErrorMessage } from "../GiftCardUpdate/messages"; import { getGiftCardErrorMessage } from "../GiftCardUpdate/messages";
import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm"; import { giftCardCreateMessages as messages } from "./messages";
import { giftCardCreateDialogMessages as messages } from "./messages";
import { useChannelCurrencies } from "./queries"; import { useChannelCurrencies } from "./queries";
import { useGiftCardCreateDialogFormStyles as useStyles } from "./styles"; import { useGiftCardCreateFormStyles as useStyles } from "./styles";
import { GiftCardCreateFormCommonProps } from "./types";
interface GiftCardCreateDialogMoneyInputProps interface GiftCardCreateMoneyInputProps {
extends GiftCardCreateFormCommonProps { change: FormChange;
set: (data: Partial<GiftCardCreateFormData>) => void; errors: GiftCardBulkCreateFormErrors;
data: Pick<GiftCardCreateCommonFormData, "balanceCurrency" | "balanceAmount">;
set: (data: Partial<GiftCardCreateCommonFormData>) => void;
} }
const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputProps> = ({ const GiftCardCreateMoneyInput: React.FC<GiftCardCreateMoneyInputProps> = ({
errors, errors,
data: { balanceAmount, balanceCurrency }, data: { balanceAmount, balanceCurrency },
change, change,
@ -32,7 +36,7 @@ const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputPro
const { channelCurrencies } = channelCurrenciesData?.shop; const { channelCurrencies } = channelCurrenciesData?.shop;
const [savedCurrency, setCurrency] = useLocalStorage( const [savedCurrency, setCurrency] = useLocalStorage(
"giftCardCreateDialogCurrency", "giftCardCreateCurrency",
undefined undefined
); );
@ -67,7 +71,7 @@ const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputPro
helperText={getGiftCardErrorMessage(errors?.balance, intl)} helperText={getGiftCardErrorMessage(errors?.balance, intl)}
change={handleInputChange} change={handleInputChange}
choices={mapSingleValueNodeToChoice(channelCurrencies)} choices={mapSingleValueNodeToChoice(channelCurrencies)}
containerClassName={classes.balanceContainer} containerClassName={classes.fullWidthContainer}
textFieldProps={{ textFieldProps={{
type: "float", type: "float",
label: intl.formatMessage(messages.amountLabel), label: intl.formatMessage(messages.amountLabel),
@ -84,4 +88,4 @@ const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputPro
); );
}; };
export default GiftCardCreateDialogMoneyInput; export default GiftCardCreateMoneyInput;

View file

@ -0,0 +1,34 @@
import { Typography } from "@material-ui/core";
import ControlledCheckbox, {
ControlledCheckboxProps
} from "@saleor/components/ControlledCheckbox";
import React from "react";
import { FormattedMessage } from "react-intl";
import { giftCardCreateMessages as messages } from "./messages";
type GiftCardCreateRequiresActivationSectionProps = Pick<
ControlledCheckboxProps,
"checked" | "onChange"
>;
const GiftCardCreateRequiresActivationSection: React.FC<GiftCardCreateRequiresActivationSectionProps> = ({
checked,
onChange
}) => (
<ControlledCheckbox
name="requiresActivation"
label={
<>
<FormattedMessage {...messages.requiresActivationLabel} />
<Typography variant="caption">
<FormattedMessage {...messages.requiresActivationCaption} />
</Typography>
</>
}
checked={checked}
onChange={onChange}
/>
);
export default GiftCardCreateRequiresActivationSection;

View file

@ -7,17 +7,19 @@ import { mapEdgesToItems } from "@saleor/utils/maps";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { giftCardCreateDialogMessages as messages } from "./messages"; import { giftCardCreateMessages as messages } from "./messages";
import { GiftCardCreateFormCustomer } from "./types"; import { GiftCardCreateFormCustomer } from "./types";
export interface GiftCardCustomerSelectFieldProps { export interface GiftCardCustomerSelectFieldProps {
selectedCustomer: GiftCardCreateFormCustomer; selectedCustomer: GiftCardCreateFormCustomer;
setSelectedCustomer: (customer: GiftCardCreateFormCustomer) => void; setSelectedCustomer: (customer: GiftCardCreateFormCustomer) => void;
disabled?: boolean;
} }
const GiftCardCustomerSelectField: React.FC<GiftCardCustomerSelectFieldProps> = ({ const GiftCardCustomerSelectField: React.FC<GiftCardCustomerSelectFieldProps> = ({
selectedCustomer, selectedCustomer,
setSelectedCustomer setSelectedCustomer,
disabled = false
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
@ -54,6 +56,7 @@ const GiftCardCustomerSelectField: React.FC<GiftCardCustomerSelectFieldProps> =
fetchChoices={search} fetchChoices={search}
onChange={handleSelect} onChange={handleSelect}
onFetchMore={loadMore} onFetchMore={loadMore}
disabled={disabled}
/> />
); );
}; };

View file

@ -1,2 +1,2 @@
export * from "./GiftCardCreateDialog"; export * from "./GiftCardCreateDialogContent";
export { default } from "./GiftCardCreateDialog"; export { default } from "./GiftCardCreateDialogContent";

View file

@ -1,53 +1,79 @@
import { defineMessages } from "react-intl"; import { defineMessages } from "react-intl";
export const giftCardCreateDialogMessages = defineMessages({ export const giftCardCreateMessages = defineMessages({
title: { title: {
defaultMessage: "Issue gift card", defaultMessage: "Issue gift card",
description: "GiftCardCreateDialog title" description: "issue gift card dialog title"
}, },
amountLabel: { amountLabel: {
defaultMessage: "Enter amount", defaultMessage: "Enter amount",
description: "GiftCardCreateDialog amount label" description: "money amount input label"
}, },
issueButtonLabel: { issueButtonLabel: {
defaultMessage: "Issue", defaultMessage: "Issue",
description: "GiftCardCreateDialog issue button label" description: "issue gift card button label"
}, },
customerLabel: { customerLabel: {
defaultMessage: "Customer", defaultMessage: "Customer",
description: "GiftCardCreateDialog customer label" description: "customer input label"
}, },
noteLabel: { noteLabel: {
defaultMessage: "Note", defaultMessage: "Note",
description: "GiftCardCreateDialog note label" description: "note input label"
}, },
noteSubtitle: { noteSubtitle: {
defaultMessage: defaultMessage:
"Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history", "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history",
description: "GiftCardCreateDialog note subtitle" description: "note input subtitle"
}, },
createdGiftCardLabel: { createdGiftCardLabel: {
defaultMessage: "This is the code of a created gift card:", defaultMessage: "This is the code of a created gift card:",
description: "GiftCardCreateDialog created gift card label" description: "created gift card code label"
}, },
copyCodeLabel: { copyCodeLabel: {
defaultMessage: "Copy code", defaultMessage: "Copy code",
description: "GiftCardCreateDialog copy code label" description: "copy code button label"
}, },
copiedToClipboardTitle: { copiedToClipboardTitle: {
defaultMessage: "Copied to clipboard", defaultMessage: "Copied to clipboard",
description: "GiftCardCreateDialog copied to clipboard title" description: "copied to clipboard alert title"
}, },
createdSuccessAlertTitle: { createdSuccessAlertTitle: {
defaultMessage: "Successfully created gift card", defaultMessage: "Successfully created gift card",
description: "GiftCardCreateDialog createdSuccessAlertTitle" description: "successfully created gift card alert title"
}, },
requiresActivationLabel: { requiresActivationLabel: {
defaultMessage: "Requires activation", defaultMessage: "Requires activation",
description: "GiftCarUpdateDetailsExpirySection requires activation label" description: "requires activation checkbox label"
}, },
requiresActivationCaption: { requiresActivationCaption: {
defaultMessage: "All issued cards require activation by staff before use.", defaultMessage: "All issued cards require activation by staff before use.",
description: "GiftCarUpdateDetailsExpirySection requires activation caption" description: "requires activation checkbox caption"
},
giftCardsAmountLabel: {
defaultMessage: "Cards Issued",
description: "issued cards amount label"
},
bulkCreateExplanation: {
defaultMessage:
"After creation Saleor will create a list of gift card codes that you will be able to download. ",
description: "gift card bulk create modal bottom explanation"
},
bulkCreateIssuedTitle: {
defaultMessage: "Bulk Issue Gift Cards",
description: "gift card bulk create success dialog title"
},
bulkCreateIssuedExplanation: {
defaultMessage:
"We have issued all of your requested gift cards. You can download the list of new gift cards using the button below.",
description: "gift card bulk create success dialog content"
},
bulkCreateIssuedAccept: {
defaultMessage: "Ok",
description: "gift card bulk create success dialog accept button"
},
bulkCreateIssuedExportToEmail: {
defaultMessage: "Export To Email",
description: "gift card bulk create success dialog export button"
} }
}); });

View file

@ -11,7 +11,7 @@ export const useGiftCardCreateDialogCodeContentStyles = makeStyles(
{ name: "GiftCardCreateDialogCodeContent" } { name: "GiftCardCreateDialogCodeContent" }
); );
export const useGiftCardCreateDialogFormStyles = makeStyles( export const useGiftCardCreateFormStyles = makeStyles(
() => ({ () => ({
noteField: { noteField: {
width: "100%" width: "100%"
@ -19,7 +19,12 @@ export const useGiftCardCreateDialogFormStyles = makeStyles(
currencySelectField: { currencySelectField: {
width: 100 width: 100
}, },
balanceContainer: { width: "100%" } fullWidthContainer: { width: "100%" },
dialogContent: {
minWidth: 550,
overflowY: "auto",
overflowX: "hidden"
}
}), }),
{ name: "GiftCardCreateDialogForm" } { name: "GiftCardCreateDialogForm" }
); );

View file

@ -10,13 +10,17 @@ export interface GiftCardCreateFormCustomer {
email: string; email: string;
} }
export type GiftCardCreateFormErrors = Record< export type GiftCardCreateCommonFormErrors = Record<
"tag" | "expiryDate" | "customer" | "currency" | "amount" | "balance", "tags" | "expiryDate" | "currency" | "amount" | "balance",
GiftCardError GiftCardError
>; >;
export type GiftCardCreateFormErrors = GiftCardCreateCommonFormErrors &
Record<"customer", GiftCardError>;
export interface GiftCardCreateFormCommonProps { export interface GiftCardCreateFormCommonProps {
change: FormChange; change: FormChange;
toggleValue: FormChange;
errors: GiftCardCreateFormErrors; errors: GiftCardCreateFormErrors;
data: GiftCardCreateFormData; data: GiftCardCreateFormData;
} }

View file

@ -1,7 +1,13 @@
import { IMessage } from "@saleor/components/messages";
import { TimePeriodTypeEnum } from "@saleor/types/globalTypes"; import { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
import commonErrorMessages from "@saleor/utils/errors/common";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { IntlShape } from "react-intl";
import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm"; import { GiftCardCreateCommonFormData } from "../GiftCardBulkCreateDialog/types";
import { giftCardUpdateFormMessages } from "../GiftCardsList/messages";
import { giftCardCreateMessages as messages } from "./messages";
import { GiftCardCreate_giftCardCreate_errors } from "./types/GiftCardCreate";
const addToCurrentDate = ( const addToCurrentDate = (
currentDate: number, currentDate: number,
@ -28,6 +34,39 @@ export const getExpiryPeriodTerminationDate = (
} }
}; };
export const getGiftCardExpiryError = (intl: IntlShape): IMessage => ({
title: intl.formatMessage(
giftCardUpdateFormMessages.giftCardInvalidExpiryDateHeader
),
text: intl.formatMessage(
giftCardUpdateFormMessages.giftCardInvalidExpiryDateContent
),
status: "error"
});
export const getGiftCardCreateOnCompletedMessage = (
errors: GiftCardCreate_giftCardCreate_errors[],
intl: IntlShape,
successMessage?: IMessage
): IMessage => {
const hasExpiryError = errors.some(error => error.field === "expiryDate");
const successGiftCardMessage = successMessage || {
status: "success",
text: intl.formatMessage(messages.createdSuccessAlertTitle)
};
if (hasExpiryError) {
return getGiftCardExpiryError(intl);
}
return !!errors?.length
? {
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
}
: successGiftCardMessage;
};
export const getGiftCardExpiryInputData = ( export const getGiftCardExpiryInputData = (
{ {
expirySelected, expirySelected,
@ -35,7 +74,7 @@ export const getGiftCardExpiryInputData = (
expiryDate, expiryDate,
expiryPeriodAmount, expiryPeriodAmount,
expiryPeriodType expiryPeriodType
}: GiftCardCreateFormData, }: GiftCardCreateCommonFormData,
currentDate: number currentDate: number
): string => { ): string => {
if (!expirySelected) { if (!expirySelected) {

View file

@ -0,0 +1,160 @@
import {
DialogActions,
DialogContent,
DialogTitle,
Typography
} from "@material-ui/core";
import ConfirmButton from "@saleor/components/ConfirmButton";
import { Task } from "@saleor/containers/BackgroundTasks/types";
import useBackgroundTask from "@saleor/hooks/useBackgroundTask";
import useForm from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier";
import ExportDialogSettings from "@saleor/products/components/ProductExportDialog/ExportDialogSettings";
import {
ExportSettingsFormData,
exportSettingsInitialFormData,
exportSettingsInitialFormDataWithIds
} from "@saleor/products/components/ProductExportDialog/types";
import { DialogProps } from "@saleor/types";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ContentWithProgress from "../GiftCardCreateDialog/ContentWithProgress";
import useGiftCardList from "../GiftCardsList/providers/GiftCardListProvider/hooks/useGiftCardList";
import useGiftCardListBulkActions from "../GiftCardsList/providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { useGiftCardTotalCountQuery } from "../GiftCardsList/queries";
import { giftCardExportDialogMessages as messages } from "./messages";
import { useGiftCardExportMutation } from "./mutations";
import { ExportGiftCards } from "./types/ExportGiftCards";
import { getExportGiftCardsInput } from "./utils";
const GiftCardExportDialog: React.FC<Pick<DialogProps, "onClose"> & {
idsToExport?: string[] | null;
}> = ({ onClose, idsToExport }) => {
const intl = useIntl();
const notify = useNotifier();
const { queue } = useBackgroundTask();
const hasIdsToExport = !!idsToExport?.length;
const {
loading: loadingGiftCardList,
totalCount: filteredGiftCardsCount
} = useGiftCardList();
const { listElements } = useGiftCardListBulkActions();
const selectedIds = idsToExport ?? listElements;
const {
data: allGiftCardsCountData,
loading: loadingGiftCardCount
} = useGiftCardTotalCountQuery();
const loading = loadingGiftCardList || loadingGiftCardCount;
const handleSubmitComplete = (data: ExportGiftCards) => {
const errors = data?.exportGiftCards?.errors;
if (!errors.length) {
notify({
text: intl.formatMessage(messages.successAlertDescription),
title: intl.formatMessage(messages.successAlertTitle)
});
queue(Task.EXPORT, {
id: data.exportGiftCards.exportFile.id
});
onClose();
}
};
const [exportGiftCards, exportGiftCardsOpts] = useGiftCardExportMutation({
onCompleted: handleSubmitComplete
});
const handleSubmit = (data: ExportSettingsFormData) => {
exportGiftCards({
variables: {
input: getExportGiftCardsInput({
data,
ids: selectedIds
})
}
});
};
const { data, change, submit } = useForm(
hasIdsToExport
? exportSettingsInitialFormDataWithIds
: exportSettingsInitialFormData,
handleSubmit
);
const allGiftCardsCount = allGiftCardsCountData?.giftCards?.totalCount;
const exportScopeLabels = {
allItems: intl.formatMessage(
{
defaultMessage: "All gift cards ({number})",
description: "export all items to csv file"
},
{
number: allGiftCardsCount || "..."
}
),
selectedItems: intl.formatMessage(
{
defaultMessage: "Selected giftCards ({number})",
description: "export selected items to csv file"
},
{
number: listElements.length
}
)
};
return (
<>
<DialogTitle>
<FormattedMessage {...messages.title} />
</DialogTitle>
<DialogContent>
<ContentWithProgress>
{!loading && (
<>
<ExportDialogSettings
errors={exportGiftCardsOpts?.data?.exportGiftCards?.errors}
onChange={change}
selectedItems={selectedIds?.length}
data={data}
exportScopeLabels={exportScopeLabels}
allowScopeSelection={!hasIdsToExport}
itemsQuantity={{
filter: filteredGiftCardsCount,
all: allGiftCardsCount
}}
/>
<Typography variant="subtitle2">
{intl.formatMessage(messages.exportNote)}
</Typography>
</>
)}
</ContentWithProgress>
</DialogContent>
<DialogActions>
<ConfirmButton
transitionState={exportGiftCardsOpts.status}
variant="contained"
type="submit"
data-test="submit"
onClick={submit}
>
<FormattedMessage {...messages.confirmButtonLabel} />
</ConfirmButton>
</DialogActions>
</>
);
};
export default GiftCardExportDialog;

View file

@ -0,0 +1,2 @@
export * from "./GiftCardExportDialogContent";
export { default } from "./GiftCardExportDialogContent";

View file

@ -0,0 +1,29 @@
import { defineMessages } from "react-intl";
export const giftCardExportDialogMessages = defineMessages({
title: {
defaultMessage: "Export Gift Card Codes",
description: "gift card export dialog title"
},
exportTypeLabel: {
defaultMessage: "gift cards",
description: "gift card export type label"
},
confirmButtonLabel: {
defaultMessage: "Export codes",
description: "gift card export dialog confirm button label"
},
successAlertDescription: {
defaultMessage:
"We are currently exporting your gift card codes. As soon as your file is available it will be sent to your email address",
description: "gift card export success alert description"
},
successAlertTitle: {
defaultMessage: "Exporting CSV",
description: "gift card export csv success alert title"
},
exportNote: {
defaultMessage: "Note: Only active and not used gift cards will be expored",
description: "note on export gift cards"
}
});

View file

@ -0,0 +1,27 @@
import { exportErrorFragment } from "@saleor/fragments/errors";
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import {
ExportGiftCards,
ExportGiftCardsVariables
} from "./types/ExportGiftCards";
const exportGiftCards = gql`
${exportErrorFragment}
mutation ExportGiftCards($input: ExportGiftCardsInput!) {
exportGiftCards(input: $input) {
errors {
...ExportErrorFragment
}
exportFile {
id
}
}
}
`;
export const useGiftCardExportMutation = makeMutation<
ExportGiftCards,
ExportGiftCardsVariables
>(exportGiftCards);

View file

@ -0,0 +1,35 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { ExportGiftCardsInput, ExportErrorCode } from "../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: ExportGiftCards
// ====================================================
export interface ExportGiftCards_exportGiftCards_errors {
__typename: "ExportError";
code: ExportErrorCode;
field: string | null;
}
export interface ExportGiftCards_exportGiftCards_exportFile {
__typename: "ExportFile";
id: string;
}
export interface ExportGiftCards_exportGiftCards {
__typename: "ExportGiftCards";
errors: ExportGiftCards_exportGiftCards_errors[];
exportFile: ExportGiftCards_exportGiftCards_exportFile | null;
}
export interface ExportGiftCards {
exportGiftCards: ExportGiftCards_exportGiftCards | null;
}
export interface ExportGiftCardsVariables {
input: ExportGiftCardsInput;
}

View file

@ -0,0 +1,27 @@
import { ExportSettingsFormData } from "@saleor/products/components/ProductExportDialog/types";
import { ExportScope } from "@saleor/types/globalTypes";
interface ExportGiftCardsInputProps {
ids: string[] | null;
data: ExportSettingsFormData;
}
export const getExportGiftCardsInput = ({
data,
ids
}: ExportGiftCardsInputProps) => {
const { scope, fileType } = data;
if (scope === ExportScope.IDS) {
return {
fileType,
scope,
ids
};
}
return {
fileType,
scope
};
};

View file

@ -3,10 +3,11 @@ import { defineMessages } from "react-intl";
export const giftCardExpirySettingsCard = defineMessages({ export const giftCardExpirySettingsCard = defineMessages({
expiryDateSectionDescription: { expiryDateSectionDescription: {
defaultMessage: defaultMessage:
"You can set gift cards to expire after a certain time after their purchase. Remember that in some countries, gift cards expiry is prohibited by law." "You can set gift cards to expire after a certain time after their purchase. Remember that in some countries, gift cards expiry is prohibited by law.",
description: "expiry date selection info message"
}, },
expiryDateTitle: { expiryDateTitle: {
defaultMessage: "Expiry date", defaultMessage: "Expiry date",
description: "section header" description: "expiry date section header"
} }
}); });

View file

@ -5,7 +5,7 @@ import { defineMessages, IntlShape } from "react-intl";
export const giftCardSettingsPageMessages = defineMessages({ export const giftCardSettingsPageMessages = defineMessages({
title: { title: {
defaultMessage: "Gift Cards Settings", defaultMessage: "Gift Cards Settings",
description: "header" description: "gift card settings header"
} }
}); });

View file

@ -0,0 +1,226 @@
import { Typography } from "@material-ui/core";
import { appPath } from "@saleor/apps/urls";
import Form from "@saleor/components/Form";
import Hr from "@saleor/components/Hr";
import Link from "@saleor/components/Link";
import Skeleton from "@saleor/components/Skeleton";
import Timeline, {
TimelineAddNote,
TimelineEvent,
TimelineNote
} from "@saleor/components/Timeline";
import { customerPath } from "@saleor/customers/urls";
import useNotifier from "@saleor/hooks/useNotifier";
import { orderPath } from "@saleor/orders/urls";
import { staffMemberDetailsPath } from "@saleor/staff/urls";
import { GiftCardEventsEnum } from "@saleor/types/globalTypes";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { IntlShape } from "react-intl";
import { useGiftCardTimelineNoteAddMutation } from "../mutations";
import { GIFT_CARD_DETAILS_QUERY } from "../queries";
import { GiftCardAddNote } from "../types/GiftCardAddNote";
import { GiftCardDetails_giftCard_events } from "../types/GiftCardDetails";
import useGiftCardHistoryEvents from "./hooks/useGiftCardHistoryEvents";
import {
giftCardHistoryMessages as messages,
giftCardHistoryTimelineMessages as timelineMessages
} from "./messages";
import useStyles from "./styles";
interface FormData {
message: string;
}
const getEventMessage = (
event: GiftCardDetails_giftCard_events,
intl: IntlShape
) => {
const getUserOrApp = () => {
if (event.user) {
const { firstName, lastName, email } = event.user;
if (lastName === "" || firstName === "") {
return email;
}
return `${firstName} ${lastName}`;
}
if (event.app) {
return event.app.name;
}
};
const getUserOrAppLink = () => {
if (event.user) {
return customerPath(event.user.id);
}
if (event.app) {
return appPath(event.app.id);
}
};
switch (event.type) {
case GiftCardEventsEnum.ACTIVATED:
return intl.formatMessage(timelineMessages.giftCardActivated, {
activatedBy: (
<Link href={staffMemberDetailsPath(event.user.id)}>
{getUserOrApp()}
</Link>
)
});
case GiftCardEventsEnum.BALANCE_RESET:
return intl.formatMessage(timelineMessages.giftCardBalanceReset, {
resetBy: (
<Link href={staffMemberDetailsPath(event.user.id)}>
{getUserOrApp()}
</Link>
)
});
case GiftCardEventsEnum.BOUGHT:
return intl.formatMessage(timelineMessages.giftCardBought, {
orderNumber: (
<Link href={orderPath(event.orderId)}>#{event.orderNumber}</Link>
)
});
case GiftCardEventsEnum.DEACTIVATED:
return intl.formatMessage(timelineMessages.giftCardDeactivated, {
deactivatedBy: (
<Link href={staffMemberDetailsPath(event.user.id)}>
{getUserOrApp()}
</Link>
)
});
case GiftCardEventsEnum.EXPIRY_DATE_UPDATED:
return intl.formatMessage(timelineMessages.giftCardExpiryDateUpdate, {
expiryUpdatedBy: (
<Link href={staffMemberDetailsPath(event.user.id)}>
{getUserOrApp()}
</Link>
)
});
case GiftCardEventsEnum.ISSUED:
return intl.formatMessage(timelineMessages.giftCardIssued, {
issuedBy: (
<Link href={staffMemberDetailsPath(event.user.id)}>
{getUserOrApp()}
</Link>
)
});
case GiftCardEventsEnum.RESENT:
return intl.formatMessage(timelineMessages.giftCardResent);
case GiftCardEventsEnum.SENT_TO_CUSTOMER:
return intl.formatMessage(timelineMessages.giftCardSentToCustomer);
case GiftCardEventsEnum.TAGS_UPDATED:
return intl.formatMessage(timelineMessages.giftCardTagsUpdated);
case GiftCardEventsEnum.UPDATED:
return intl.formatMessage(timelineMessages.giftCardTagsUpdated);
case GiftCardEventsEnum.USED_IN_ORDER:
return intl.formatMessage(timelineMessages.giftCardUsedInOrder, {
orderLink: (
<Link href={orderPath(event.orderId)}>#{event.orderNumber}</Link>
),
buyer: content =>
getUserOrApp() ? (
<Link
href={getUserOrAppLink()}
>{`${content} ${getUserOrApp()}`}</Link>
) : null
});
}
};
const GiftCardHistory: React.FC = () => {
const intl = useIntl();
const notify = useNotifier();
const { id, events } = useGiftCardHistoryEvents();
const classes = useStyles();
const onTimelineNoteAddCompleted = ({ giftCardAddNote }: GiftCardAddNote) => {
const { errors } = giftCardAddNote;
if (errors.length > 0) {
notify({
status: "error",
text: intl.formatMessage(messages.noteAddError)
});
} else {
notify({
status: "success",
text: intl.formatMessage(messages.noteAddedSuccessfully)
});
}
};
const [addTimelineNote, { loading }] = useGiftCardTimelineNoteAddMutation({
refetchQueries: [GIFT_CARD_DETAILS_QUERY],
onCompleted: onTimelineNoteAddCompleted
});
const onNoteAdd = (data: FormData) => {
const { message } = data;
addTimelineNote({ variables: { id, input: { message } } });
};
return (
<div className={classes.root}>
<Typography className={classes.header} color="textSecondary">
<FormattedMessage {...messages.historyHeaderTitle} />
</Typography>
<Hr />
<Timeline>
<Skeleton>
{events && (
<>
<Form
initial={{ message: "" }}
onSubmit={onNoteAdd}
resetOnSubmit
>
{({ change, data, reset, submit }) => (
<TimelineAddNote
message={data.message}
reset={reset}
onChange={change}
onSubmit={submit}
disabled={loading}
/>
)}
</Form>
{events
.slice()
.reverse()
.map(event => {
const { id, message, type, date, user } = event;
if (type === GiftCardEventsEnum.NOTE_ADDED) {
return (
<TimelineNote
date={date}
user={user}
message={message}
key={id}
/>
);
}
return (
<TimelineEvent
key={id}
date={date}
title={getEventMessage(event, intl)}
/>
);
})}
</>
)}
</Skeleton>
</Timeline>
</div>
);
};
export default GiftCardHistory;

View file

@ -0,0 +1,14 @@
import { useContext } from "react";
import { GiftCardDetailsContext } from "../../providers/GiftCardDetailsProvider";
const useGiftCardHistoryEvents = () => {
const { giftCard } = useContext(GiftCardDetailsContext);
return {
id: giftCard?.id,
events: giftCard?.events
};
};
export default useGiftCardHistoryEvents;

View file

@ -0,0 +1,62 @@
import { defineMessages } from "react-intl";
const giftCardHistoryMessages = defineMessages({
noteAddedSuccessfully: {
defaultMessage: "Note was added sucessfully",
description: "notifier message"
},
noteAddError: {
defaultMessage: "There was an error adding a note",
description: "notifier message"
},
historyHeaderTitle: {
defaultMessage: "Gift Card Timeline",
description: "section header title"
}
});
const giftCardHistoryTimelineMessages = defineMessages({
giftCardActivated: {
defaultMessage: "Gift card was activated by {activatedBy}",
description: "gift card history message"
},
giftCardBalanceReset: {
defaultMessage: "Gift card balance was reset by {resetBy}",
description: "gift card history message"
},
giftCardBought: {
defaultMessage: "Gift card was bought in order {orderNumber}",
description: "gift card history message"
},
giftCardDeactivated: {
defaultMessage: "Gift card was deactivated by {deactivatedBy}",
description: "gift card history message"
},
giftCardExpiryDateUpdate: {
defaultMessage: "Gift card expiry date was updated by {expiryUpdatedBy}",
description: "gift card history message"
},
giftCardIssued: {
defaultMessage: "Gift card was issued by {issuedBy}",
description: "dsc"
},
giftCardResent: {
defaultMessage: "Gift card was resent",
description: "gift card history message"
},
giftCardSentToCustomer: {
defaultMessage: "Gift card was sent to customer",
description: "gift card history message"
},
giftCardTagsUpdated: {
defaultMessage: "Gift card tags were updated",
description: "gift card history message"
},
giftCardUsedInOrder: {
defaultMessage:
"Gift card was used as a payment method on order {orderLink} <buyer>by</buyer>",
description: "gift card history message"
}
});
export { giftCardHistoryMessages, giftCardHistoryTimelineMessages };

View file

@ -0,0 +1,14 @@
import { makeStyles } from "@saleor/macaw-ui";
const useStyles = makeStyles(
theme => ({
header: {
fontWeight: 500,
marginBottom: theme.spacing(1)
},
root: { marginTop: theme.spacing(4) }
}),
{ name: "GiftCardHistory" }
);
export default useStyles;

View file

@ -9,8 +9,8 @@ import SingleAutocompleteSelectField from "@saleor/components/SingleAutocomplete
import useForm from "@saleor/hooks/useForm"; import useForm from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { getBySlug } from "@saleor/products/components/ProductVariantCreatorPage/utils"; import { getBySlug } from "@saleor/products/components/ProductVariantCreatorPage/utils";
import { DialogProps } from "@saleor/types";
import commonErrorMessages from "@saleor/utils/errors/common"; import commonErrorMessages from "@saleor/utils/errors/common";
import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers";
import { mapSlugNodeToChoice } from "@saleor/utils/maps"; import { mapSlugNodeToChoice } from "@saleor/utils/maps";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -29,10 +29,7 @@ export interface GiftCardResendCodeFormData {
channelSlug: string; channelSlug: string;
} }
const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({ const GiftCardResendCodeDialog: React.FC<DialogProps> = ({ open, onClose }) => {
open,
closeDialog
}) => {
const intl = useIntl(); const intl = useIntl();
const notify = useNotifier(); const notify = useNotifier();
const classes = useStyles(); const classes = useStyles();
@ -99,7 +96,7 @@ const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({
notify(notifierData); notify(notifierData);
if (!errors.length) { if (!errors.length) {
closeDialog(); onClose();
reset(); reset();
} }
}; };
@ -128,7 +125,7 @@ const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({
open={open} open={open}
onConfirm={submit} onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.submitButtonLabel)} confirmButtonLabel={intl.formatMessage(messages.submitButtonLabel)}
onClose={closeDialog} onClose={onClose}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
confirmButtonState={status} confirmButtonState={status}
disabled={loading} disabled={loading}

View file

@ -3,31 +3,31 @@ import { defineMessages } from "react-intl";
export const giftCardResendCodeDialogMessages = defineMessages({ export const giftCardResendCodeDialogMessages = defineMessages({
title: { title: {
defaultMessage: "Resend code to customer", defaultMessage: "Resend code to customer",
description: "GiftCardResendCodeDialog title" description: "resend code to customer title"
}, },
description: { description: {
defaultMessage: defaultMessage:
"Gift Card Code will be resent to email provided during checkout. You can provide a different email address if you want to:", "Gift Card Code will be resent to email provided during checkout. You can provide a different email address if you want to:",
description: "GiftCardResendCodeDialog description" description: "resend code to customer description"
}, },
consentCheckboxLabel: { consentCheckboxLabel: {
defaultMessage: "Yes, I want to send gift card to different address", defaultMessage: "Yes, I want to send gift card to different address",
description: "GiftCardResendCodeDialog consentCheckboxLabel" description: "consent to send gift card to different address checkbox label"
}, },
submitButtonLabel: { submitButtonLabel: {
defaultMessage: "Resend", defaultMessage: "Resend",
description: "GiftCardResendCodeDialog submitButtonLabel" description: "resend button label"
}, },
emailInputPlaceholder: { emailInputPlaceholder: {
defaultMessage: "Provided email address", defaultMessage: "Provided email address",
description: "GiftCardResendCodeDialog emailInputPlaceholder" description: "provided email input placeholder"
}, },
successResendAlertText: { successResendAlertText: {
defaultMessage: "Successfully resent code to customer!", defaultMessage: "Successfully resent code to customer!",
description: "GiftCardResendCodeDialog successResendAlertText" description: "resent code success message"
}, },
sendToChannelSelectLabel: { sendToChannelSelectLabel: {
defaultMessage: "Send to channel", defaultMessage: "Send to channel",
description: "ChannelPickerSelectField sendToChannelLabel" description: "send to channel select label"
} }
}); });

View file

@ -65,11 +65,16 @@ export interface GiftCardResend_giftCardResend_giftCard_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardResend_giftCardResend_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardResend_giftCardResend_giftCard { export interface GiftCardResend_giftCardResend_giftCard {
__typename: "GiftCard"; __typename: "GiftCard";
metadata: (GiftCardResend_giftCardResend_giftCard_metadata | null)[]; metadata: (GiftCardResend_giftCardResend_giftCard_metadata | null)[];
privateMetadata: (GiftCardResend_giftCardResend_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardResend_giftCardResend_giftCard_privateMetadata | null)[];
displayCode: string; last4CodeChars: string;
boughtInChannel: string | null; boughtInChannel: string | null;
createdBy: GiftCardResend_giftCardResend_giftCard_createdBy | null; createdBy: GiftCardResend_giftCardResend_giftCard_createdBy | null;
product: GiftCardResend_giftCardResend_giftCard_product | null; product: GiftCardResend_giftCardResend_giftCard_product | null;
@ -84,7 +89,7 @@ export interface GiftCardResend_giftCardResend_giftCard {
initialBalance: GiftCardResend_giftCardResend_giftCard_initialBalance | null; initialBalance: GiftCardResend_giftCardResend_giftCard_initialBalance | null;
currentBalance: GiftCardResend_giftCardResend_giftCard_currentBalance | null; currentBalance: GiftCardResend_giftCardResend_giftCard_currentBalance | null;
id: string; id: string;
tag: string | null; tags: GiftCardResend_giftCardResend_giftCard_tags[] | null;
} }
export interface GiftCardResend_giftCardResend { export interface GiftCardResend_giftCardResend {

View file

@ -4,8 +4,8 @@ import CardSpacer from "@saleor/components/CardSpacer";
import { IMessage } from "@saleor/components/messages"; import { IMessage } from "@saleor/components/messages";
import useForm from "@saleor/hooks/useForm"; import useForm from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { DialogProps } from "@saleor/types";
import commonErrorMessages from "@saleor/utils/errors/common"; import commonErrorMessages from "@saleor/utils/errors/common";
import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -22,9 +22,9 @@ export interface GiftCardBalanceUpdateFormData {
balanceAmount: number; balanceAmount: number;
} }
const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlersProps> = ({ const GiftCardUpdateBalanceDialog: React.FC<DialogProps> = ({
open, open,
closeDialog onClose
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
@ -57,7 +57,7 @@ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlersProps> = ({
notify(notifierData); notify(notifierData);
if (!errors.length) { if (!errors.length) {
closeDialog(); onClose();
} }
}; };
@ -103,7 +103,7 @@ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlersProps> = ({
open={open} open={open}
onConfirm={submit} onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.changeButtonLabel)} confirmButtonLabel={intl.formatMessage(messages.changeButtonLabel)}
onClose={closeDialog} onClose={onClose}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
confirmButtonState={status} confirmButtonState={status}
disabled={loading || !hasChanged} disabled={loading || !hasChanged}

View file

@ -3,20 +3,19 @@ import { defineMessages } from "react-intl";
export const giftCardUpdateBalanceDialogMessages = defineMessages({ export const giftCardUpdateBalanceDialogMessages = defineMessages({
title: { title: {
defaultMessage: "Set balance", defaultMessage: "Set balance",
description: "GiftCardUpdateDetailsCard set balance button label" description: "set balance dialog title label"
}, },
subtitle: { subtitle: {
defaultMessage: defaultMessage:
"What would you like to set cards balance to. When you change the balance both values will be changed", "What would you like to set cards balance to. When you change the balance both values will be changed",
description: "GiftCardUpdateDetailsCard set balance dialog subtitle" description: "set balance dialog subtitle"
}, },
updatedSuccessAlertTitle: { updatedSuccessAlertTitle: {
defaultMessage: "Successfully updated card balance", defaultMessage: "Successfully updated card balance",
description: "GiftCardUpdateDetailsCard update success alert title" description: "card update success alert title"
}, },
changeButtonLabel: { changeButtonLabel: {
defaultMessage: "Change", defaultMessage: "Change",
description: description: "change button label"
"GiftCardUpdateDetailsCard set balance dialog change button label"
} }
}); });

View file

@ -26,8 +26,8 @@ const GiftCardUpdateDetailsCard: React.FC = () => {
const { loading, giftCard } = useGiftCardDetails(); const { loading, giftCard } = useGiftCardDetails();
const { openSetBalanceDialog } = useGiftCardUpdateDialogs(); const { openSetBalanceDialog } = useGiftCardUpdateDialogs();
const { const {
change, toggleValue,
data: { tag }, data: { tags },
formErrors formErrors
} = useGiftCardUpdateForm(); } = useGiftCardUpdateForm();
@ -61,10 +61,10 @@ const GiftCardUpdateDetailsCard: React.FC = () => {
</Typography> </Typography>
<VerticalSpacer /> <VerticalSpacer />
<GiftCardTagInput <GiftCardTagInput
error={formErrors?.tag} error={formErrors?.tags}
name="tag" name="tags"
value={tag} values={tags}
change={change} toggleChange={toggleValue}
/> />
<CardSpacer /> <CardSpacer />
<GiftCardUpdateExpirySelect /> <GiftCardUpdateExpirySelect />

View file

@ -3,18 +3,18 @@ import { defineMessages } from "react-intl";
export const giftCardUpdateDetailsCardMessages = defineMessages({ export const giftCardUpdateDetailsCardMessages = defineMessages({
title: { title: {
defaultMessage: "Details", defaultMessage: "Details",
description: "GiftCardUpdateDetailsCard title" description: "details title"
}, },
setBalanceButtonLabel: { setBalanceButtonLabel: {
defaultMessage: "set balance", defaultMessage: "set balance",
description: "GiftCardUpdateDetailsCard set balance button label" description: "set balance button label"
}, },
cardBalanceLabel: { cardBalanceLabel: {
defaultMessage: "Card Balance", defaultMessage: "Card Balance",
description: "GiftCardUpdateDetailsCard card balance label" description: "card balance label"
}, },
tagInputLabel: { tagInputLabel: {
defaultMessage: "Card Tag", defaultMessage: "Card Tag",
description: "GiftCardTagInput tag label" description: "tag label"
} }
}); });

View file

@ -3,14 +3,14 @@ import { defineMessages } from "react-intl";
export const giftCardExpirySelectMessages = defineMessages({ export const giftCardExpirySelectMessages = defineMessages({
expiryDateCheckboxLabel: { expiryDateCheckboxLabel: {
defaultMessage: "Gift card expires", defaultMessage: "Gift card expires",
description: "GiftCarUpdateDetailsExpirySection expiry date checkbox label" description: "expiry date checkbox label"
}, },
expiryDateLabel: { expiryDateLabel: {
defaultMessage: "Expiration date", defaultMessage: "Expiration date",
description: "GiftCarUpdateDetailsExpirySection expiry date label" description: "expiration date label"
}, },
expiredOnLabel: { expiredOnLabel: {
defaultMessage: "Expired on {date}", defaultMessage: "Expired on {date}",
description: "GiftCarUpdateDetailsExpirySection expired on label" description: "expired on label"
} }
}); });

View file

@ -4,7 +4,6 @@ import CardSpacer from "@saleor/components/CardSpacer";
import Link from "@saleor/components/Link"; import Link from "@saleor/components/Link";
import { customerUrl } from "@saleor/customers/urls"; import { customerUrl } from "@saleor/customers/urls";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import useNavigator from "@saleor/hooks/useNavigator";
import { getFullName, getStringOrPlaceholder } from "@saleor/misc"; import { getFullName, getStringOrPlaceholder } from "@saleor/misc";
import Label from "@saleor/orders/components/OrderHistory/Label"; import Label from "@saleor/orders/components/OrderHistory/Label";
import { getOrderNumberLinkObject } from "@saleor/orders/components/OrderHistory/utils"; import { getOrderNumberLinkObject } from "@saleor/orders/components/OrderHistory/utils";
@ -22,7 +21,6 @@ import { giftCardUpdateInfoCardMessages as messages } from "./messages";
const GiftCardUpdateInfoCardContent: React.FC = () => { const GiftCardUpdateInfoCardContent: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const localizeDate = useDateLocalize(); const localizeDate = useDateLocalize();
const navigate = useNavigator();
const { giftCard } = useGiftCardDetails(); const { giftCard } = useGiftCardDetails();
@ -122,7 +120,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(messages.orderNumberLabel)} /> <Label text={intl.formatMessage(messages.orderNumberLabel)} />
{orderData ? ( {orderData ? (
<Link onClick={() => navigate(orderData.link)}>{orderData.text}</Link> <Link href={orderData.link}>{orderData.text}</Link>
) : ( ) : (
<Typography>{PLACEHOLDER}</Typography> <Typography>{PLACEHOLDER}</Typography>
)} )}
@ -130,9 +128,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(messages.productLabel)} /> <Label text={intl.formatMessage(messages.productLabel)} />
{product ? ( {product ? (
<Link onClick={() => navigate(productUrl(product?.id))}> <Link href={productUrl(product?.id)}>{product?.name}</Link>
{product?.name}
</Link>
) : ( ) : (
<Typography>{PLACEHOLDER}</Typography> <Typography>{PLACEHOLDER}</Typography>
)} )}
@ -140,7 +136,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(buyerLabelMessage)} /> <Label text={intl.formatMessage(buyerLabelMessage)} />
{buyerUrl ? ( {buyerUrl ? (
<Link onClick={() => navigate(buyerUrl)}>{buyerName}</Link> <Link href={buyerUrl}>{buyerName}</Link>
) : ( ) : (
<Typography>{buyerName}</Typography> <Typography>{buyerName}</Typography>
)} )}
@ -148,9 +144,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(messages.usedByLabel)} /> <Label text={intl.formatMessage(messages.usedByLabel)} />
{usedBy ? ( {usedBy ? (
<Link onClick={() => navigate(customerUrl(usedBy.id))}> <Link href={customerUrl(usedBy.id)}>{getFullName(usedBy)}</Link>
{getFullName(usedBy)}
</Link>
) : ( ) : (
<Typography> <Typography>
{getStringOrPlaceholder(usedByEmail, PLACEHOLDER)} {getStringOrPlaceholder(usedByEmail, PLACEHOLDER)}

View file

@ -3,34 +3,34 @@ import { defineMessages } from "react-intl";
export const giftCardUpdateInfoCardMessages = defineMessages({ export const giftCardUpdateInfoCardMessages = defineMessages({
title: { title: {
defaultMessage: "Card information", defaultMessage: "Card information",
description: "GiftCardUpdateInfoCard title" description: "info card title"
}, },
creationLabel: { creationLabel: {
defaultMessage: "Creation", defaultMessage: "Creation",
description: "GiftCardUpdateInfoCard creation label" description: "creation label"
}, },
orderNumberLabel: { orderNumberLabel: {
defaultMessage: "Order number", defaultMessage: "Order number",
description: "GiftCardUpdateInfoCard order number label" description: "order number label"
}, },
productLabel: { productLabel: {
defaultMessage: "Product bought to get gift card", defaultMessage: "Product bought to get gift card",
description: "GiftCardUpdateInfoCard product label" description: "product label"
}, },
issuedByLabel: { issuedByLabel: {
defaultMessage: "Issued by", defaultMessage: "Issued by",
description: "GiftCardUpdateInfoCard issued by label" description: "issued by label"
}, },
issuedByAppLabel: { issuedByAppLabel: {
defaultMessage: "Issued by app", defaultMessage: "Issued by app",
description: "GiftCardUpdateInfoCard issued by app label" description: "issued by app label"
}, },
boughtByLabel: { boughtByLabel: {
defaultMessage: "Bought by", defaultMessage: "Bought by",
description: "GiftCardUpdateInfoCard bought by label" description: "bought by label"
}, },
usedByLabel: { usedByLabel: {
defaultMessage: "Used by", defaultMessage: "Used by",
description: "GiftCardUpdateInfoCard used by label" description: "used by label"
} }
}); });

View file

@ -5,6 +5,7 @@ import Metadata from "@saleor/components/Metadata";
import Savebar from "@saleor/components/Savebar"; import Savebar from "@saleor/components/Savebar";
import React from "react"; import React from "react";
import GiftCardHistory from "./GiftCardHistory/GiftCardHistory";
import GiftCardUpdateDetailsCard from "./GiftCardUpdateDetailsCard"; import GiftCardUpdateDetailsCard from "./GiftCardUpdateDetailsCard";
import GiftCardUpdateInfoCard from "./GiftCardUpdateInfoCard"; import GiftCardUpdateInfoCard from "./GiftCardUpdateInfoCard";
import GiftCardUpdatePageHeader from "./GiftCardUpdatePageHeader"; import GiftCardUpdatePageHeader from "./GiftCardUpdatePageHeader";
@ -38,6 +39,7 @@ const GiftCardUpdatePage: React.FC = () => {
<div> <div>
<GiftCardUpdateInfoCard /> <GiftCardUpdateInfoCard />
</div> </div>
<GiftCardHistory />
</Grid> </Grid>
<Savebar <Savebar
state={status} state={status}

View file

@ -1,22 +1,13 @@
import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { ConfirmButton } from "@saleor/macaw-ui"; import { ConfirmButton } from "@saleor/macaw-ui";
import commonErrorMessages from "@saleor/utils/errors/common";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages"; import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages";
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
import { giftCardEnableDisableSectionMessages as messages } from "./messages"; import useGiftCardActivateToggle from "./hooks/useGiftCardActivateToggle";
import {
useGiftCardActivateMutation,
useGiftCardDeactivateMutation
} from "./mutations";
import { GiftCardActivate } from "./types/GiftCardActivate";
import { GiftCardDeactivate } from "./types/GiftCardDeactivate";
const GiftCardEnableDisableSection: React.FC = () => { const GiftCardEnableDisableSection: React.FC = () => {
const notify = useNotifier();
const intl = useIntl(); const intl = useIntl();
const { const {
@ -27,50 +18,12 @@ const GiftCardEnableDisableSection: React.FC = () => {
return null; return null;
} }
const onActivateCompleted = (data: GiftCardActivate) => { const {
const errors = data?.giftCardActivate?.errors; giftCardActivate,
if (!!errors?.length) {
notify({
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
});
return;
}
notify({
status: "success",
text: intl.formatMessage(messages.successfullyEnabledTitle)
});
};
const onDeactivateCompleted = (data: GiftCardDeactivate) => {
const errors = data?.giftCardDeactivate?.errors;
if (!!errors?.length) {
notify({
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
});
return;
}
notify({
status: "success",
text: intl.formatMessage(messages.successfullyDisabledTitle)
});
};
const [giftCardActivate, giftCardActivateOpts] = useGiftCardActivateMutation({
onCompleted: onActivateCompleted
});
const [
giftCardDeactivate, giftCardDeactivate,
giftCardDeactivateOpts currentOpts
] = useGiftCardDeactivateMutation({ } = useGiftCardActivateToggle({
onCompleted: onDeactivateCompleted isActive
}); });
const handleClick = () => const handleClick = () =>
@ -82,8 +35,6 @@ const GiftCardEnableDisableSection: React.FC = () => {
? buttonMessages.disableLabel ? buttonMessages.disableLabel
: buttonMessages.enableLabel; : buttonMessages.enableLabel;
const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts;
return ( return (
<ConfirmButton <ConfirmButton
data-test-id="enable-button" data-test-id="enable-button"

View file

@ -2,7 +2,7 @@ import { Button } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer"; import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip"; import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip";
import { StatusType } from "@saleor/components/StatusChip/types"; import GiftCardStatusChip from "@saleor/giftCards/components/GiftCardStatusChip/GiftCardStatusChip";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { Backlink } from "@saleor/macaw-ui"; import { Backlink } from "@saleor/macaw-ui";
import React from "react"; import React from "react";
@ -25,42 +25,25 @@ const GiftCardUpdatePageHeader: React.FC = () => {
const { openResendCodeDialog } = useGiftCardUpdateDialogs(); const { openResendCodeDialog } = useGiftCardUpdateDialogs();
const { displayCode, isActive, isExpired } = giftCard; const { last4CodeChars, isExpired } = giftCard;
const title = intl.formatMessage(tableMessages.codeEndingWithLabel, { const title = intl.formatMessage(tableMessages.codeEndingWithLabel, {
displayCode last4CodeChars
}); });
const getPageTitle = () => {
if (isExpired) {
return (
<PageTitleWithStatusChip
title={title}
statusLabel={intl.formatMessage(messages.expiredStatusLabel)}
statusType={StatusType.NEUTRAL}
/>
);
}
if (!isActive) {
return (
<PageTitleWithStatusChip
title={title}
statusLabel={intl.formatMessage(messages.disabledStatusLabel)}
statusType={StatusType.ERROR}
/>
);
}
return title;
};
return ( return (
<> <>
<Backlink onClick={navigateBack}> <Backlink onClick={navigateBack}>
{intl.formatMessage(sectionNames.giftCards)} {intl.formatMessage(sectionNames.giftCards)}
</Backlink> </Backlink>
<PageHeader inline title={getPageTitle()}> <PageHeader
inline
title={
<PageTitleWithStatusChip title={title}>
<GiftCardStatusChip giftCard={giftCard} />
</PageTitleWithStatusChip>
}
>
<GiftCardEnableDisableSection /> <GiftCardEnableDisableSection />
<HorizontalSpacer /> <HorizontalSpacer />
{!isExpired && ( {!isExpired && (

View file

@ -0,0 +1,95 @@
import useNotifier from "@saleor/hooks/useNotifier";
import commonErrorMessages from "@saleor/utils/errors/common";
import { useIntl } from "react-intl";
import { GIFT_CARD_DETAILS_QUERY } from "../../queries";
import { giftCardEnableDisableSectionMessages as messages } from "../messages";
import {
useGiftCardActivateMutation,
useGiftCardDeactivateMutation
} from "../mutations";
import { GiftCardActivate } from "../types/GiftCardActivate";
import { GiftCardDeactivate } from "../types/GiftCardDeactivate";
interface useGiftCardActivateToggleProps {
onActivateActionComplete?: () => void | undefined;
onDeactivateActionComplete?: () => void | undefined;
isActive?: boolean;
}
const useGiftCardActivateToggle = ({
onActivateActionComplete,
onDeactivateActionComplete,
isActive
}: useGiftCardActivateToggleProps) => {
const intl = useIntl();
const notify = useNotifier();
const onActivateCompleted = (data: GiftCardActivate) => {
const errors = data?.giftCardActivate?.errors;
if (!!errors?.length) {
notify({
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
});
return;
}
notify({
status: "success",
text: intl.formatMessage(messages.successfullyEnabledTitle)
});
if (!!onActivateActionComplete) {
onActivateActionComplete();
}
};
const onDeactivateCompleted = (data: GiftCardDeactivate) => {
const errors = data?.giftCardDeactivate?.errors;
if (!!errors?.length) {
notify({
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
});
return;
}
notify({
status: "success",
text: intl.formatMessage(messages.successfullyDisabledTitle)
});
if (!!onDeactivateActionComplete) {
onDeactivateActionComplete();
}
};
const [giftCardActivate, giftCardActivateOpts] = useGiftCardActivateMutation({
onCompleted: onActivateCompleted,
refetchQueries: [GIFT_CARD_DETAILS_QUERY]
});
const [
giftCardDeactivate,
giftCardDeactivateOpts
] = useGiftCardDeactivateMutation({
onCompleted: onDeactivateCompleted,
refetchQueries: [GIFT_CARD_DETAILS_QUERY]
});
const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts;
return {
giftCardActivate,
giftCardActivateOpts,
giftCardDeactivate,
giftCardDeactivateOpts,
currentOpts
};
};
export default useGiftCardActivateToggle;

View file

@ -3,25 +3,25 @@ import { defineMessages } from "react-intl";
export const giftCardEnableDisableSectionMessages = defineMessages({ export const giftCardEnableDisableSectionMessages = defineMessages({
successfullyEnabledTitle: { successfullyEnabledTitle: {
defaultMessage: "Successfully enabled gift card", defaultMessage: "Successfully enabled gift card",
description: "GiftCardEnableDisableSection enable success" description: "success gift card enable message"
}, },
successfullyDisabledTitle: { successfullyDisabledTitle: {
defaultMessage: "Successfully disabled gift card", defaultMessage: "Successfully disabled gift card",
description: "GiftCardEnableDisableSection disable success" description: "success gift card disable message"
} }
}); });
export const giftCardUpdatePageHeaderMessages = defineMessages({ export const giftCardUpdatePageHeaderMessages = defineMessages({
resendButtonLabel: { resendButtonLabel: {
defaultMessage: "Resend code", defaultMessage: "Resend code",
description: "giftCardUpdatePageHeader resendButtonLabel" description: "resend code label"
}, },
expiredStatusLabel: { expiredStatusLabel: {
defaultMessage: "Expired", defaultMessage: "Expired",
description: "giftCardUpdatePageHeader expired status label" description: "expired status label"
}, },
disabledStatusLabel: { disabledStatusLabel: {
defaultMessage: "Disabled", defaultMessage: "Disabled",
description: "giftCardUpdatePageHeader disabled status label" description: "disabled status label"
} }
}); });

View file

@ -65,11 +65,16 @@ export interface GiftCardActivate_giftCardActivate_giftCard_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardActivate_giftCardActivate_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardActivate_giftCardActivate_giftCard { export interface GiftCardActivate_giftCardActivate_giftCard {
__typename: "GiftCard"; __typename: "GiftCard";
metadata: (GiftCardActivate_giftCardActivate_giftCard_metadata | null)[]; metadata: (GiftCardActivate_giftCardActivate_giftCard_metadata | null)[];
privateMetadata: (GiftCardActivate_giftCardActivate_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardActivate_giftCardActivate_giftCard_privateMetadata | null)[];
displayCode: string; last4CodeChars: string;
boughtInChannel: string | null; boughtInChannel: string | null;
createdBy: GiftCardActivate_giftCardActivate_giftCard_createdBy | null; createdBy: GiftCardActivate_giftCardActivate_giftCard_createdBy | null;
product: GiftCardActivate_giftCardActivate_giftCard_product | null; product: GiftCardActivate_giftCardActivate_giftCard_product | null;
@ -84,7 +89,7 @@ export interface GiftCardActivate_giftCardActivate_giftCard {
initialBalance: GiftCardActivate_giftCardActivate_giftCard_initialBalance | null; initialBalance: GiftCardActivate_giftCardActivate_giftCard_initialBalance | null;
currentBalance: GiftCardActivate_giftCardActivate_giftCard_currentBalance | null; currentBalance: GiftCardActivate_giftCardActivate_giftCard_currentBalance | null;
id: string; id: string;
tag: string | null; tags: GiftCardActivate_giftCardActivate_giftCard_tags[] | null;
} }
export interface GiftCardActivate_giftCardActivate { export interface GiftCardActivate_giftCardActivate {

View file

@ -65,11 +65,16 @@ export interface GiftCardDeactivate_giftCardDeactivate_giftCard_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardDeactivate_giftCardDeactivate_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardDeactivate_giftCardDeactivate_giftCard { export interface GiftCardDeactivate_giftCardDeactivate_giftCard {
__typename: "GiftCard"; __typename: "GiftCard";
metadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_metadata | null)[]; metadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_metadata | null)[];
privateMetadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_privateMetadata | null)[];
displayCode: string; last4CodeChars: string;
boughtInChannel: string | null; boughtInChannel: string | null;
createdBy: GiftCardDeactivate_giftCardDeactivate_giftCard_createdBy | null; createdBy: GiftCardDeactivate_giftCardDeactivate_giftCard_createdBy | null;
product: GiftCardDeactivate_giftCardDeactivate_giftCard_product | null; product: GiftCardDeactivate_giftCardDeactivate_giftCard_product | null;
@ -84,7 +89,7 @@ export interface GiftCardDeactivate_giftCardDeactivate_giftCard {
initialBalance: GiftCardDeactivate_giftCardDeactivate_giftCard_initialBalance | null; initialBalance: GiftCardDeactivate_giftCardDeactivate_giftCard_initialBalance | null;
currentBalance: GiftCardDeactivate_giftCardDeactivate_giftCard_currentBalance | null; currentBalance: GiftCardDeactivate_giftCardDeactivate_giftCard_currentBalance | null;
id: string; id: string;
tag: string | null; tags: GiftCardDeactivate_giftCardDeactivate_giftCard_tags[] | null;
} }
export interface GiftCardDeactivate_giftCardDeactivate { export interface GiftCardDeactivate_giftCardDeactivate {

View file

@ -1,19 +1,21 @@
import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { GiftCardErrorCode } from "@saleor/types/globalTypes"; import { GiftCardErrorCode } from "@saleor/types/globalTypes";
import { getCommonFormFieldErrorMessage } from "@saleor/utils/errors/common"; import commonErrorMessages, {
getCommonFormFieldErrorMessage
} from "@saleor/utils/errors/common";
import { defineMessages, IntlShape } from "react-intl"; import { defineMessages, IntlShape } from "react-intl";
export const giftCardUpdateDetailsCardMessages = defineMessages({ export const giftCardUpdateDetailsCardMessages = defineMessages({
title: { title: {
defaultMessage: "Details", defaultMessage: "Details",
description: "GiftCardUpdateDetailsCard title" description: "title"
} }
}); });
const giftCardErrorMessages = defineMessages({ const giftCardErrorMessages = defineMessages({
notFound: { notFound: {
defaultMessage: "Couldn't find gift card", defaultMessage: "Couldn't find gift card",
description: "giftCardErrorMessages not found" description: "gift card not found message"
} }
}); });
@ -25,6 +27,8 @@ export function getGiftCardErrorMessage(
switch (error.code) { switch (error.code) {
case GiftCardErrorCode.NOT_FOUND: case GiftCardErrorCode.NOT_FOUND:
return intl.formatMessage(giftCardErrorMessages.notFound); return intl.formatMessage(giftCardErrorMessages.notFound);
case GiftCardErrorCode.INVALID:
return intl.formatMessage(commonErrorMessages.invalid);
} }
} }

View file

@ -2,7 +2,11 @@ import { giftCardErrorFragment } from "@saleor/fragments/errors";
import makeMutation from "@saleor/hooks/makeMutation"; import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag"; import gql from "graphql-tag";
import { giftCardDataFragment } from "./queries"; import { giftCardDataFragment, giftCardEventsFragment } from "./queries";
import {
GiftCardAddNote,
GiftCardAddNoteVariables
} from "./types/GiftCardAddNote";
import { import {
GiftCardUpdate, GiftCardUpdate,
GiftCardUpdateVariables GiftCardUpdateVariables
@ -11,6 +15,7 @@ import {
const giftCardUpdate = gql` const giftCardUpdate = gql`
${giftCardDataFragment} ${giftCardDataFragment}
${giftCardErrorFragment} ${giftCardErrorFragment}
${giftCardEventsFragment}
mutation GiftCardUpdate($id: ID!, $input: GiftCardUpdateInput!) { mutation GiftCardUpdate($id: ID!, $input: GiftCardUpdateInput!) {
giftCardUpdate(id: $id, input: $input) { giftCardUpdate(id: $id, input: $input) {
errors { errors {
@ -18,6 +23,9 @@ const giftCardUpdate = gql`
} }
giftCard { giftCard {
...GiftCardData ...GiftCardData
events {
...GiftCardEvent
}
} }
} }
} }
@ -27,3 +35,28 @@ export const useGiftCardUpdateMutation = makeMutation<
GiftCardUpdate, GiftCardUpdate,
GiftCardUpdateVariables GiftCardUpdateVariables
>(giftCardUpdate); >(giftCardUpdate);
export const giftCardTimelineNoteAdd = gql`
${giftCardDataFragment}
${giftCardErrorFragment}
${giftCardEventsFragment}
mutation GiftCardAddNote($id: ID!, $input: GiftCardAddNoteInput!) {
giftCardAddNote(id: $id, input: $input) {
errors {
...GiftCardError
message
}
giftCard {
...GiftCardData
}
event {
...GiftCardEvent
}
}
}
`;
export const useGiftCardTimelineNoteAddMutation = makeMutation<
GiftCardAddNote,
GiftCardAddNoteVariables
>(giftCardTimelineNoteAdd);

View file

@ -20,7 +20,7 @@ interface GiftCardUpdateDialogsProviderProps {
export interface GiftCardUpdateDialogsConsumerProps { export interface GiftCardUpdateDialogsConsumerProps {
navigateBack: () => void; navigateBack: () => void;
closeDialog: () => void; onClose: () => void;
openSetBalanceDialog: () => void; openSetBalanceDialog: () => void;
openDeleteDialog: () => void; openDeleteDialog: () => void;
openResendCodeDialog: () => void; openResendCodeDialog: () => void;
@ -45,7 +45,7 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
RESEND_CODE RESEND_CODE
} = GiftCardUpdatePageActionParamsEnum; } = GiftCardUpdatePageActionParamsEnum;
const [openDialog, closeDialog] = createDialogActionHandlers< const [openDialog, onClose] = createDialogActionHandlers<
GiftCardUpdatePageActionParamsEnum, GiftCardUpdatePageActionParamsEnum,
GiftCardUpdatePageUrlQueryParams GiftCardUpdatePageUrlQueryParams
>(navigate, params => giftCardUrl(id, params), params); >(navigate, params => giftCardUrl(id, params), params);
@ -59,7 +59,7 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
openSetBalanceDialog: () => openDialog(SET_BALANCE), openSetBalanceDialog: () => openDialog(SET_BALANCE),
openDeleteDialog: () => openDialog(DELETE), openDeleteDialog: () => openDialog(DELETE),
openResendCodeDialog: () => openDialog(RESEND_CODE), openResendCodeDialog: () => openDialog(RESEND_CODE),
closeDialog, onClose,
navigateBack navigateBack
}; };
@ -69,17 +69,17 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
{!loadingGiftCard && ( {!loadingGiftCard && (
<> <>
<GiftCardUpdateBalanceDialog <GiftCardUpdateBalanceDialog
closeDialog={closeDialog} onClose={onClose}
open={isDialogOpen(SET_BALANCE)} open={isDialogOpen(SET_BALANCE)}
/> />
<GiftCardUpdatePageDeleteDialog <GiftCardUpdatePageDeleteDialog
closeDialog={closeDialog} onClose={onClose}
open={isDialogOpen(DELETE)} open={isDialogOpen(DELETE)}
navigateBack={navigateBack} navigateBack={navigateBack}
/> />
<GiftCardResendCodeDialog <GiftCardResendCodeDialog
open={isDialogOpen(RESEND_CODE)} open={isDialogOpen(RESEND_CODE)}
closeDialog={closeDialog} onClose={onClose}
/> />
</> </>
)} )}

View file

@ -1,5 +1,6 @@
import { MetadataFormData } from "@saleor/components/Metadata"; import { MetadataFormData } from "@saleor/components/Metadata";
import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { giftCardUpdateFormMessages } from "@saleor/giftCards/GiftCardsList/messages";
import { MutationResultWithOpts } from "@saleor/hooks/makeMutation"; import { MutationResultWithOpts } from "@saleor/hooks/makeMutation";
import useForm, { FormChange, UseFormResult } from "@saleor/hooks/useForm"; import useForm, { FormChange, UseFormResult } from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
@ -14,6 +15,7 @@ import {
usePrivateMetadataUpdate usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata"; } from "@saleor/utils/metadata/updateMetadata";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger"; import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import difference from "lodash/difference";
import React, { createContext } from "react"; import React, { createContext } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -30,7 +32,7 @@ interface GiftCardUpdateFormProviderProps {
} }
export type GiftCardUpdateFormData = MetadataFormData & export type GiftCardUpdateFormData = MetadataFormData &
Pick<GiftCardCreateFormData, "tag" | "expiryDate">; Pick<GiftCardCreateFormData, "tags" | "expiryDate">;
export interface GiftCardUpdateFormConsumerData export interface GiftCardUpdateFormConsumerData
extends GiftCardUpdateFormErrors { extends GiftCardUpdateFormErrors {
@ -38,7 +40,7 @@ export interface GiftCardUpdateFormConsumerData
} }
export interface GiftCardUpdateFormErrors { export interface GiftCardUpdateFormErrors {
formErrors: Record<"tag" | "expiryDate", GiftCardError>; formErrors: Record<"tags" | "expiryDate", GiftCardError>;
handlers: { changeMetadata: FormChange }; handlers: { changeMetadata: FormChange };
} }
@ -51,6 +53,19 @@ export const GiftCardUpdateFormContext = createContext<
GiftCardUpdateFormConsumerProps GiftCardUpdateFormConsumerProps
>(null); >(null);
const getGiftCardTagsAddRemoveData = (
initTags: string[],
changedTags: string[]
) => {
const removed = difference(initTags, changedTags);
const added = difference(changedTags, initTags);
return {
addTags: added,
removeTags: removed
};
};
const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
children children
}) => { }) => {
@ -66,10 +81,10 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
return { ...emptyFormData, metadata: [], privateMetadata: [] }; return { ...emptyFormData, metadata: [], privateMetadata: [] };
} }
const { tag, expiryDate, privateMetadata, metadata } = giftCard; const { tags, expiryDate, privateMetadata, metadata } = giftCard;
return { return {
tag, tags: tags.map(({ name }) => name),
expiryDate, expiryDate,
privateMetadata: privateMetadata?.map(mapMetadataItemToInput), privateMetadata: privateMetadata?.map(mapMetadataItemToInput),
metadata: metadata?.map(mapMetadataItemToInput) metadata: metadata?.map(mapMetadataItemToInput)
@ -78,21 +93,37 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
const onSubmit = (data: GiftCardUpdate) => { const onSubmit = (data: GiftCardUpdate) => {
const errors = data.giftCardUpdate.errors; const errors = data.giftCardUpdate.errors;
const hasExpiryError = errors.some(error => error.field === "expiryDate");
notify(getDefaultNotifierSuccessErrorData(errors, intl)); notify(
hasExpiryError
? {
title: intl.formatMessage(
giftCardUpdateFormMessages.giftCardInvalidExpiryDateHeader
),
text: intl.formatMessage(
giftCardUpdateFormMessages.giftCardInvalidExpiryDateContent
),
status: "error"
}
: getDefaultNotifierSuccessErrorData(errors, intl)
);
}; };
const [updateGiftCard, updateGiftCardOpts] = useGiftCardUpdateMutation({ const [updateGiftCard, updateGiftCardOpts] = useGiftCardUpdateMutation({
onCompleted: onSubmit onCompleted: onSubmit
}); });
const submit = async ({ tag, expiryDate }: GiftCardUpdateFormData) => { const submit = async ({ tags, expiryDate }: GiftCardUpdateFormData) => {
const result = await updateGiftCard({ const result = await updateGiftCard({
variables: { variables: {
id: giftCard?.id, id: giftCard?.id,
input: { input: {
tag, expiryDate,
expiryDate ...getGiftCardTagsAddRemoveData(
giftCard.tags.map(el => el.name),
tags
)
} }
} }
}); });
@ -128,7 +159,7 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
handleFormSubmit(submitData, handleSubmit, setChanged); handleFormSubmit(submitData, handleSubmit, setChanged);
const formErrors = getFormErrors( const formErrors = getFormErrors(
["tag", "expiryDate"], ["tags", "expiryDate"],
updateGiftCardOpts?.data?.giftCardUpdate?.errors updateGiftCardOpts?.data?.giftCardUpdate?.errors
); );

View file

@ -15,7 +15,7 @@ export const giftCardDataFragment = gql`
${fragmentUserBase} ${fragmentUserBase}
fragment GiftCardData on GiftCard { fragment GiftCardData on GiftCard {
...MetadataFragment ...MetadataFragment
displayCode last4CodeChars
boughtInChannel boughtInChannel
createdBy { createdBy {
...UserBase ...UserBase
@ -48,7 +48,9 @@ export const giftCardDataFragment = gql`
} }
id id
tag tags {
name
}
} }
`; `;
@ -65,17 +67,17 @@ export const giftCardDetails = gql`
type type
user { user {
...UserBase ...UserBase
email
} }
app { app {
id id
name name
} }
message message
email
orderId orderId
orderNumber orderNumber
tag tags
oldTag oldTags
balance { balance {
initialBalance { initialBalance {
...Money ...Money
@ -95,7 +97,46 @@ export const giftCardDetails = gql`
} }
`; `;
export const giftCardEventsFragment = gql`
fragment GiftCardEvent on GiftCardEvent {
expiryDate
oldExpiryDate
id
date
type
user {
...UserBase
}
app {
id
name
}
message
email
orderId
orderNumber
tags
oldTags
balance {
initialBalance {
...Money
}
currentBalance {
...Money
}
oldInitialBalance {
...Money
}
oldCurrentBalance {
...Money
}
}
}
`;
export const useGiftCardDetailsQuery = makeQuery< export const useGiftCardDetailsQuery = makeQuery<
GiftCardDetails, GiftCardDetails,
GiftCardDetailsVariables GiftCardDetailsVariables
>(giftCardDetails); >(giftCardDetails);
export const GIFT_CARD_DETAILS_QUERY = "GiftCardDetails";

View file

@ -0,0 +1,173 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardAddNoteInput, GiftCardErrorCode, GiftCardEventsEnum } from "./../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: GiftCardAddNote
// ====================================================
export interface GiftCardAddNote_giftCardAddNote_errors {
__typename: "GiftCardError";
code: GiftCardErrorCode;
field: string | null;
message: string | null;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_createdBy {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_product {
__typename: "Product";
id: string;
name: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_usedBy {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_app {
__typename: "App";
id: string;
name: string | null;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_initialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_currentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardAddNote_giftCardAddNote_giftCard {
__typename: "GiftCard";
metadata: (GiftCardAddNote_giftCardAddNote_giftCard_metadata | null)[];
privateMetadata: (GiftCardAddNote_giftCardAddNote_giftCard_privateMetadata | null)[];
last4CodeChars: string;
boughtInChannel: string | null;
createdBy: GiftCardAddNote_giftCardAddNote_giftCard_createdBy | null;
product: GiftCardAddNote_giftCardAddNote_giftCard_product | null;
usedBy: GiftCardAddNote_giftCardAddNote_giftCard_usedBy | null;
usedByEmail: string | null;
createdByEmail: string | null;
app: GiftCardAddNote_giftCardAddNote_giftCard_app | null;
created: any;
expiryDate: any | null;
lastUsedOn: any | null;
isActive: boolean;
initialBalance: GiftCardAddNote_giftCardAddNote_giftCard_initialBalance | null;
currentBalance: GiftCardAddNote_giftCardAddNote_giftCard_currentBalance | null;
id: string;
tags: GiftCardAddNote_giftCardAddNote_giftCard_tags[] | null;
}
export interface GiftCardAddNote_giftCardAddNote_event_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardAddNote_giftCardAddNote_event_app {
__typename: "App";
id: string;
name: string | null;
}
export interface GiftCardAddNote_giftCardAddNote_event_balance_initialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardAddNote_giftCardAddNote_event_balance_currentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardAddNote_giftCardAddNote_event_balance_oldInitialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardAddNote_giftCardAddNote_event_balance_oldCurrentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardAddNote_giftCardAddNote_event_balance {
__typename: "GiftCardEventBalance";
initialBalance: GiftCardAddNote_giftCardAddNote_event_balance_initialBalance | null;
currentBalance: GiftCardAddNote_giftCardAddNote_event_balance_currentBalance;
oldInitialBalance: GiftCardAddNote_giftCardAddNote_event_balance_oldInitialBalance | null;
oldCurrentBalance: GiftCardAddNote_giftCardAddNote_event_balance_oldCurrentBalance | null;
}
export interface GiftCardAddNote_giftCardAddNote_event {
__typename: "GiftCardEvent";
expiryDate: any | null;
oldExpiryDate: any | null;
id: string;
date: any | null;
type: GiftCardEventsEnum | null;
user: GiftCardAddNote_giftCardAddNote_event_user | null;
app: GiftCardAddNote_giftCardAddNote_event_app | null;
message: string | null;
email: string | null;
orderId: string | null;
orderNumber: string | null;
tags: string[] | null;
oldTags: string[] | null;
balance: GiftCardAddNote_giftCardAddNote_event_balance | null;
}
export interface GiftCardAddNote_giftCardAddNote {
__typename: "GiftCardAddNote";
errors: GiftCardAddNote_giftCardAddNote_errors[];
giftCard: GiftCardAddNote_giftCardAddNote_giftCard | null;
event: GiftCardAddNote_giftCardAddNote_event | null;
}
export interface GiftCardAddNote {
giftCardAddNote: GiftCardAddNote_giftCardAddNote | null;
}
export interface GiftCardAddNoteVariables {
id: string;
input: GiftCardAddNoteInput;
}

View file

@ -57,11 +57,16 @@ export interface GiftCardData_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardData_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardData { export interface GiftCardData {
__typename: "GiftCard"; __typename: "GiftCard";
metadata: (GiftCardData_metadata | null)[]; metadata: (GiftCardData_metadata | null)[];
privateMetadata: (GiftCardData_privateMetadata | null)[]; privateMetadata: (GiftCardData_privateMetadata | null)[];
displayCode: string; last4CodeChars: string;
boughtInChannel: string | null; boughtInChannel: string | null;
createdBy: GiftCardData_createdBy | null; createdBy: GiftCardData_createdBy | null;
product: GiftCardData_product | null; product: GiftCardData_product | null;
@ -76,5 +81,5 @@ export interface GiftCardData {
initialBalance: GiftCardData_initialBalance | null; initialBalance: GiftCardData_initialBalance | null;
currentBalance: GiftCardData_currentBalance | null; currentBalance: GiftCardData_currentBalance | null;
id: string; id: string;
tag: string | null; tags: GiftCardData_tags[] | null;
} }

View file

@ -59,11 +59,17 @@ export interface GiftCardDetails_giftCard_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardDetails_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardDetails_giftCard_events_user { export interface GiftCardDetails_giftCard_events_user {
__typename: "User"; __typename: "User";
id: string; id: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
email: string;
} }
export interface GiftCardDetails_giftCard_events_app { export interface GiftCardDetails_giftCard_events_app {
@ -114,11 +120,10 @@ export interface GiftCardDetails_giftCard_events {
user: GiftCardDetails_giftCard_events_user | null; user: GiftCardDetails_giftCard_events_user | null;
app: GiftCardDetails_giftCard_events_app | null; app: GiftCardDetails_giftCard_events_app | null;
message: string | null; message: string | null;
email: string | null;
orderId: string | null; orderId: string | null;
orderNumber: string | null; orderNumber: string | null;
tag: string | null; tags: string[] | null;
oldTag: string | null; oldTags: string[] | null;
balance: GiftCardDetails_giftCard_events_balance | null; balance: GiftCardDetails_giftCard_events_balance | null;
} }
@ -126,7 +131,7 @@ export interface GiftCardDetails_giftCard {
__typename: "GiftCard"; __typename: "GiftCard";
metadata: (GiftCardDetails_giftCard_metadata | null)[]; metadata: (GiftCardDetails_giftCard_metadata | null)[];
privateMetadata: (GiftCardDetails_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardDetails_giftCard_privateMetadata | null)[];
displayCode: string; last4CodeChars: string;
boughtInChannel: string | null; boughtInChannel: string | null;
createdBy: GiftCardDetails_giftCard_createdBy | null; createdBy: GiftCardDetails_giftCard_createdBy | null;
product: GiftCardDetails_giftCard_product | null; product: GiftCardDetails_giftCard_product | null;
@ -141,7 +146,7 @@ export interface GiftCardDetails_giftCard {
initialBalance: GiftCardDetails_giftCard_initialBalance | null; initialBalance: GiftCardDetails_giftCard_initialBalance | null;
currentBalance: GiftCardDetails_giftCard_currentBalance | null; currentBalance: GiftCardDetails_giftCard_currentBalance | null;
id: string; id: string;
tag: string | null; tags: GiftCardDetails_giftCard_tags[] | null;
events: GiftCardDetails_giftCard_events[]; events: GiftCardDetails_giftCard_events[];
} }

View file

@ -0,0 +1,73 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardEventsEnum } from "./../../../types/globalTypes";
// ====================================================
// GraphQL fragment: GiftCardEvent
// ====================================================
export interface GiftCardEvent_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardEvent_app {
__typename: "App";
id: string;
name: string | null;
}
export interface GiftCardEvent_balance_initialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardEvent_balance_currentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardEvent_balance_oldInitialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardEvent_balance_oldCurrentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardEvent_balance {
__typename: "GiftCardEventBalance";
initialBalance: GiftCardEvent_balance_initialBalance | null;
currentBalance: GiftCardEvent_balance_currentBalance;
oldInitialBalance: GiftCardEvent_balance_oldInitialBalance | null;
oldCurrentBalance: GiftCardEvent_balance_oldCurrentBalance | null;
}
export interface GiftCardEvent {
__typename: "GiftCardEvent";
expiryDate: any | null;
oldExpiryDate: any | null;
id: string;
date: any | null;
type: GiftCardEventsEnum | null;
user: GiftCardEvent_user | null;
app: GiftCardEvent_app | null;
message: string | null;
email: string | null;
orderId: string | null;
orderNumber: string | null;
tags: string[] | null;
oldTags: string[] | null;
balance: GiftCardEvent_balance | null;
}

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { GiftCardUpdateInput, GiftCardErrorCode } from "./../../../types/globalTypes"; import { GiftCardUpdateInput, GiftCardErrorCode, GiftCardEventsEnum } from "./../../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: GiftCardUpdate // GraphQL mutation operation: GiftCardUpdate
@ -65,11 +65,79 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardUpdate_giftCardUpdate_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_app {
__typename: "App";
id: string;
name: string | null;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_balance_initialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_balance_currentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_balance_oldInitialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_balance_oldCurrentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events_balance {
__typename: "GiftCardEventBalance";
initialBalance: GiftCardUpdate_giftCardUpdate_giftCard_events_balance_initialBalance | null;
currentBalance: GiftCardUpdate_giftCardUpdate_giftCard_events_balance_currentBalance;
oldInitialBalance: GiftCardUpdate_giftCardUpdate_giftCard_events_balance_oldInitialBalance | null;
oldCurrentBalance: GiftCardUpdate_giftCardUpdate_giftCard_events_balance_oldCurrentBalance | null;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_events {
__typename: "GiftCardEvent";
expiryDate: any | null;
oldExpiryDate: any | null;
id: string;
date: any | null;
type: GiftCardEventsEnum | null;
user: GiftCardUpdate_giftCardUpdate_giftCard_events_user | null;
app: GiftCardUpdate_giftCardUpdate_giftCard_events_app | null;
message: string | null;
email: string | null;
orderId: string | null;
orderNumber: string | null;
tags: string[] | null;
oldTags: string[] | null;
balance: GiftCardUpdate_giftCardUpdate_giftCard_events_balance | null;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard { export interface GiftCardUpdate_giftCardUpdate_giftCard {
__typename: "GiftCard"; __typename: "GiftCard";
metadata: (GiftCardUpdate_giftCardUpdate_giftCard_metadata | null)[]; metadata: (GiftCardUpdate_giftCardUpdate_giftCard_metadata | null)[];
privateMetadata: (GiftCardUpdate_giftCardUpdate_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardUpdate_giftCardUpdate_giftCard_privateMetadata | null)[];
displayCode: string; last4CodeChars: string;
boughtInChannel: string | null; boughtInChannel: string | null;
createdBy: GiftCardUpdate_giftCardUpdate_giftCard_createdBy | null; createdBy: GiftCardUpdate_giftCardUpdate_giftCard_createdBy | null;
product: GiftCardUpdate_giftCardUpdate_giftCard_product | null; product: GiftCardUpdate_giftCardUpdate_giftCard_product | null;
@ -84,7 +152,8 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard {
initialBalance: GiftCardUpdate_giftCardUpdate_giftCard_initialBalance | null; initialBalance: GiftCardUpdate_giftCardUpdate_giftCard_initialBalance | null;
currentBalance: GiftCardUpdate_giftCardUpdate_giftCard_currentBalance | null; currentBalance: GiftCardUpdate_giftCardUpdate_giftCard_currentBalance | null;
id: string; id: string;
tag: string | null; tags: GiftCardUpdate_giftCardUpdate_giftCard_tags[] | null;
events: GiftCardUpdate_giftCardUpdate_giftCard_events[];
} }
export interface GiftCardUpdate_giftCardUpdate { export interface GiftCardUpdate_giftCardUpdate {

View file

@ -1,13 +1,17 @@
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import Container from "@saleor/components/Container"; import Container from "@saleor/components/Container";
import React from "react"; import React from "react";
import GiftCardsListHeader from "./GiftCardsListHeader"; import GiftCardsListHeader from "./GiftCardsListHeader";
import GiftCardsListOrderInfoCard from "./GiftCardsListOrderInfoCard/GiftCardsListOrderInfoCard";
import GiftCardsListTable from "./GiftCardsListTable"; import GiftCardsListTable from "./GiftCardsListTable";
const GiftCardsListPage: React.FC = () => ( const GiftCardsListPage: React.FC = () => (
<Container> <Container>
<GiftCardsListHeader /> <GiftCardsListHeader />
<GiftCardsListTable /> <GiftCardsListTable />
<VerticalSpacer spacing={2} />
<GiftCardsListOrderInfoCard />
</Container> </Container>
); );

View file

@ -43,7 +43,7 @@ const GiftCardListSearchAndFilters: React.FC = () => {
const { reset } = useGiftCardListBulkActions(); const { reset } = useGiftCardListBulkActions();
const { const {
closeDialog, onClose,
openSearchDeleteDialog, openSearchDeleteDialog,
openSearchSaveDialog openSearchSaveDialog
} = useGiftCardListDialogs(); } = useGiftCardListDialogs();
@ -98,7 +98,7 @@ const GiftCardListSearchAndFilters: React.FC = () => {
}, },
tags: compact( tags: compact(
mapEdgesToItems(searchGiftCardTagsResult?.data?.search)?.map( mapEdgesToItems(searchGiftCardTagsResult?.data?.search)?.map(
({ tag }) => tag ({ name }) => name
) )
) )
}); });
@ -168,13 +168,13 @@ const GiftCardListSearchAndFilters: React.FC = () => {
<SaveFilterTabDialog <SaveFilterTabDialog
open={params.action === GiftCardListActionParamsEnum.SAVE_SEARCH} open={params.action === GiftCardListActionParamsEnum.SAVE_SEARCH}
confirmButtonState="default" confirmButtonState="default"
onClose={closeDialog} onClose={onClose}
onSubmit={handleTabSave} onSubmit={handleTabSave}
/> />
<DeleteFilterTabDialog <DeleteFilterTabDialog
open={params.action === GiftCardListActionParamsEnum.DELETE_SEARCH} open={params.action === GiftCardListActionParamsEnum.DELETE_SEARCH}
confirmButtonState="default" confirmButtonState="default"
onClose={closeDialog} onClose={onClose}
onSubmit={handleTabDelete} onSubmit={handleTabDelete}
tabName={maybe(() => tabs[currentTab - 1].name, "...")} tabName={maybe(() => tabs[currentTab - 1].name, "...")}
/> />

View file

@ -62,7 +62,6 @@ export const getFilterOpts = ({
value: params?.currency, value: params?.currency,
choices: mapSingleValueNodeToChoice(currencies), choices: mapSingleValueNodeToChoice(currencies),
displayValues: mapSingleValueNodeToChoice(currencies), displayValues: mapSingleValueNodeToChoice(currencies),
initialSearch: "",
loading: loadingCurrencies loading: loadingCurrencies
}, },
product: { product: {
@ -222,7 +221,8 @@ export function createFilterStructure(
multiple: multiple:
opts?.initialBalanceAmount?.value?.min !== opts?.initialBalanceAmount?.value?.min !==
opts?.initialBalanceAmount?.value?.max, opts?.initialBalanceAmount?.value?.max,
active: opts.initialBalanceAmount.active active: opts.initialBalanceAmount.active,
dependencies: [GiftCardListFilterKeys.currency]
}, },
{ {
@ -234,23 +234,16 @@ export function createFilterStructure(
multiple: multiple:
opts?.currentBalanceAmount?.value?.min !== opts?.currentBalanceAmount?.value?.min !==
opts?.currentBalanceAmount?.value?.max, opts?.currentBalanceAmount?.value?.max,
active: opts.currentBalanceAmount.active active: opts.currentBalanceAmount.active,
dependencies: [GiftCardListFilterKeys.currency]
}, },
{ {
...createAutocompleteField( ...createOptionsField(
GiftCardListFilterKeys.currency, GiftCardListFilterKeys.currency,
intl.formatMessage(messages.currencyLabel), intl.formatMessage(messages.currencyLabel),
[opts.currency.value], [opts.currency.value],
opts.currency.displayValues,
false, false,
opts.currency.choices, opts.currency.choices
{
hasMore: opts.currency.hasMore,
initialSearch: "",
loading: opts.currency.loading,
onFetchMore: opts.currency.onFetchMore,
onSearchChange: opts.currency.onSearchChange
}
), ),
active: opts.currency.active active: opts.currency.active
}, },

View file

@ -3,21 +3,21 @@ import { defineMessages } from "react-intl";
export const giftCardListFilterErrorMessages = defineMessages({ export const giftCardListFilterErrorMessages = defineMessages({
balanceAmount: { balanceAmount: {
defaultMessage: "Balance amount is missing", defaultMessage: "Balance amount is missing",
description: "Filter balance amount error" description: "balance amound missing error message"
}, },
balanceCurrency: { balanceCurrency: {
defaultMessage: "Balance currency is missing", defaultMessage: "Balance currency is missing",
description: "Filter balance currency error" description: "balance curreny missing error message"
} }
}); });
export const giftCardListSearchAndFiltersMessages = defineMessages({ export const giftCardListSearchAndFiltersMessages = defineMessages({
searchPlaceholder: { searchPlaceholder: {
defaultMessage: "Search Gift Cards, e.g {exampleGiftCardCode}", defaultMessage: "Search Gift Cards, e.g {exampleGiftCardCode}",
description: "gift card search placeholder" description: "search gift card placeholder"
}, },
defaultTabLabel: { defaultTabLabel: {
defaultMessage: "All Gift Cards", defaultMessage: "All Gift Cards",
description: "gift card default tab label" description: "all gift cards label"
} }
}); });

View file

@ -17,7 +17,11 @@ const GiftCardsListHeader: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const navigate = useNavigator(); const navigate = useNavigator();
const { openCreateDialog } = useGiftCardListDialogs(); const {
openCreateDialog,
openBulkCreateDialog,
openExportDialog
} = useGiftCardListDialogs();
const openSettings = () => navigate(giftCardSettingsUrl); const openSettings = () => navigate(giftCardSettingsUrl);
@ -26,17 +30,17 @@ const GiftCardsListHeader: React.FC = () => {
label: intl.formatMessage(messages.settings), label: intl.formatMessage(messages.settings),
testId: "settingsMenuItem", testId: "settingsMenuItem",
onSelect: openSettings onSelect: openSettings
},
{
label: intl.formatMessage(messages.bulkIssue),
testId: "bulkIssueMenuItem",
onSelect: openBulkCreateDialog
},
{
label: intl.formatMessage(messages.exportCodes),
testId: "exportCodesMenuItem",
onSelect: openExportDialog
} }
// {
// label: intl.formatMessage(messages.bulkIssue),
// testId: "bulkIssueMenuItem"
// // onSelect:
// },
// {
// label: intl.formatMessage(messages.exportCodes),
// testId: "exportCodesMenuItem"
// // onSelect:
// }
]; ];
return ( return (

View file

@ -1,8 +1,4 @@
import useNavigator from "@saleor/hooks/useNavigator";
import { Alert } from "@saleor/macaw-ui"; import { Alert } from "@saleor/macaw-ui";
import { productAddUrl } from "@saleor/products/urls";
import { productTypeAddUrl } from "@saleor/productTypes/urls";
import { ProductTypeKindEnum } from "@saleor/types/globalTypes";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -12,7 +8,6 @@ import GiftCardsListHeaderAlertContent from "./GiftCardsListHeaderAlertContent";
const GiftCardsListHeaderAlert: React.FC = () => { const GiftCardsListHeaderAlert: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const navigate = useNavigator();
const { const {
data: giftCardProductsCount, data: giftCardProductsCount,
@ -28,15 +23,6 @@ const GiftCardsListHeaderAlert: React.FC = () => {
!giftCardProductsCountLoading && !giftCardProductsCountLoading &&
(!giftCardProductTypesExist || !giftCardProductsExist); (!giftCardProductTypesExist || !giftCardProductsExist);
const handleCreateGiftCardProductType = () =>
navigate(
productTypeAddUrl({
kind: ProductTypeKindEnum.GIFT_CARD
})
);
const handleCreateGiftCardProduct = () => navigate(productAddUrl());
if (showNoGiftCardProductsAlert) { if (showNoGiftCardProductsAlert) {
return ( return (
<Alert <Alert
@ -47,8 +33,6 @@ const GiftCardsListHeaderAlert: React.FC = () => {
<GiftCardsListHeaderAlertContent <GiftCardsListHeaderAlertContent
giftCardProductTypesExist={giftCardProductTypesExist} giftCardProductTypesExist={giftCardProductTypesExist}
giftCardProductsExist={giftCardProductsExist} giftCardProductsExist={giftCardProductsExist}
handleCreateGiftCardProductType={handleCreateGiftCardProductType}
handleCreateGiftCardProduct={handleCreateGiftCardProduct}
/> />
</Alert> </Alert>
); );

View file

@ -1,4 +1,7 @@
import Link from "@saleor/components/Link"; import Link from "@saleor/components/Link";
import { productAddUrl } from "@saleor/products/urls";
import { productTypeAddUrl } from "@saleor/productTypes/urls";
import { ProductTypeKindEnum } from "@saleor/types/globalTypes";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
@ -8,28 +11,27 @@ import { useHeaderStyles as useStyles } from "../styles";
interface GiftCardsListHeaderAlertContentProps { interface GiftCardsListHeaderAlertContentProps {
giftCardProductTypesExist: boolean; giftCardProductTypesExist: boolean;
giftCardProductsExist: boolean; giftCardProductsExist: boolean;
handleCreateGiftCardProductType: () => void;
handleCreateGiftCardProduct: () => void;
} }
const GiftCardsListHeaderAlertContent: React.FC<GiftCardsListHeaderAlertContentProps> = ({ const GiftCardsListHeaderAlertContent: React.FC<GiftCardsListHeaderAlertContentProps> = ({
giftCardProductTypesExist, giftCardProductTypesExist,
giftCardProductsExist, giftCardProductsExist
handleCreateGiftCardProductType,
handleCreateGiftCardProduct
}) => { }) => {
const classes = useStyles({}); const classes = useStyles({});
const giftCardProductTypeUrl = productTypeAddUrl({
kind: ProductTypeKindEnum.GIFT_CARD
});
const giftCardCreateGiftCardProductUrl = productAddUrl();
if (!giftCardProductTypesExist) { if (!giftCardProductTypesExist) {
return ( return (
<FormattedMessage <FormattedMessage
{...messages.noGiftCardsProductTypes} {...messages.noGiftCardsProductTypes}
values={{ values={{
createGiftCardProductType: ( createGiftCardProductType: (
<Link <Link href={giftCardProductTypeUrl} className={classes.alertLink}>
onClick={handleCreateGiftCardProductType}
className={classes.alertLink}
>
<FormattedMessage {...messages.createGiftCardProductType} /> <FormattedMessage {...messages.createGiftCardProductType} />
</Link> </Link>
) )
@ -45,7 +47,7 @@ const GiftCardsListHeaderAlertContent: React.FC<GiftCardsListHeaderAlertContentP
values={{ values={{
createGiftCardProduct: ( createGiftCardProduct: (
<Link <Link
onClick={handleCreateGiftCardProduct} href={giftCardCreateGiftCardProductUrl}
className={classes.alertLink} className={classes.alertLink}
> >
<FormattedMessage {...messages.createGiftCardProduct} /> <FormattedMessage {...messages.createGiftCardProduct} />

View file

@ -0,0 +1,25 @@
import { Typography } from "@material-ui/core";
import Link from "@saleor/components/Link";
import { Alert } from "@saleor/macaw-ui";
import { orderGiftCardBoughtPath } from "@saleor/orders/urls";
import React from "react";
import { FormattedMessage } from "react-intl";
import { giftCardListOrderCardMessages as messages } from "./messages";
const GiftCardsListOrderInfoCard: React.FC = () => (
<Alert variant="info" close={false}>
<Typography>
<FormattedMessage
{...messages.giftCardOrderInfoMessage}
values={{
link: content => (
<Link href={orderGiftCardBoughtPath()}>{content}</Link>
)
}}
/>
</Typography>
</Alert>
);
export default GiftCardsListOrderInfoCard;

View file

@ -0,0 +1,9 @@
import { defineMessages } from "react-intl";
export const giftCardListOrderCardMessages = defineMessages({
giftCardOrderInfoMessage: {
defaultMessage:
"Gift cards will appear after their order is fullfilled. <link>View Orders with Gift Cards</link>",
description: "alert card message"
}
});

View file

@ -11,156 +11,144 @@ import DeleteIconButton from "@saleor/components/DeleteIconButton";
import Link from "@saleor/components/Link"; import Link from "@saleor/components/Link";
import ResponsiveTable from "@saleor/components/ResponsiveTable"; import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import StatusChip from "@saleor/components/StatusChip";
import { StatusType } from "@saleor/components/StatusChip/types";
import { customerUrl } from "@saleor/customers/urls"; import { customerUrl } from "@saleor/customers/urls";
import GiftCardStatusChip from "@saleor/giftCards/components/GiftCardStatusChip/GiftCardStatusChip";
import { PLACEHOLDER } from "@saleor/giftCards/GiftCardUpdate/types"; import { PLACEHOLDER } from "@saleor/giftCards/GiftCardUpdate/types";
import { giftCardUrl } from "@saleor/giftCards/urls"; import { giftCardListUrl, giftCardUrl } from "@saleor/giftCards/urls";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import { renderCollection } from "@saleor/misc"; import { renderCollection } from "@saleor/misc";
import { productUrl } from "@saleor/products/urls"; import { productUrl } from "@saleor/products/urls";
import React from "react"; import React from "react";
import { useEffect } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
import GiftCardListSearchAndFilters from "../GiftCardListSearchAndFilters"; import GiftCardListSearchAndFilters from "../GiftCardListSearchAndFilters";
import { giftCardsListTableMessages as messages } from "../messages"; import { giftCardsListTableMessages as messages } from "../messages";
import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs"; import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList"; import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
import useGiftCardListBulkActions from "../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions"; import useGiftCardListBulkActions from "../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { canBeSorted } from "../sort";
import { useTableStyles as useStyles } from "../styles"; import { useTableStyles as useStyles } from "../styles";
import { GiftCardUrlSortField } from "../types";
import GiftCardsListTableFooter from "./GiftCardsListTableFooter"; import GiftCardsListTableFooter from "./GiftCardsListTableFooter";
import GiftCardsListTableHeader from "./GiftCardsListTableHeader"; import GiftCardsListTableHeader from "./GiftCardsListTableHeader";
import { getTagCellText } from "./utils";
const GiftCardsListTable: React.FC = () => { const GiftCardsListTable: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
const navigate = useNavigator(); const navigate = useNavigator();
const { giftCards, numberOfColumns, loading } = useGiftCardList(); const { giftCards, numberOfColumns, loading, params } = useGiftCardList();
const { toggle, isSelected } = useGiftCardListBulkActions(); const { toggle, isSelected } = useGiftCardListBulkActions();
const { openDeleteDialog } = useGiftCardListDialogs(); const { openDeleteDialog } = useGiftCardListDialogs();
const isCurrencySelected = !!params.currency;
useEffect(() => {
if (!canBeSorted(params.sort, isCurrencySelected)) {
navigate(
giftCardListUrl({
...params,
sort: GiftCardUrlSortField.usedBy
})
);
}
});
const redirectToGiftCardUpdate = (id: string) => () => const redirectToGiftCardUpdate = (id: string) => () =>
navigate(giftCardUrl(id)); navigate(giftCardUrl(id));
const selectGiftCardStatusChip = ({
isActive,
isExpired
}: {
isActive: boolean;
isExpired: boolean;
}) => {
if (isExpired) {
return (
<StatusChip
size="md"
status={StatusType.NEUTRAL}
label={intl.formatMessage(
giftCardStatusChipMessages.expiredStatusLabel
)}
/>
);
}
if (!isActive) {
return (
<StatusChip
size="md"
status={StatusType.ERROR}
label={intl.formatMessage(
giftCardStatusChipMessages.disabledStatusLabel
)}
/>
);
}
};
return ( return (
<Card> <Card>
<GiftCardListSearchAndFilters /> <GiftCardListSearchAndFilters />
<ResponsiveTable> <ResponsiveTable>
<GiftCardsListTableHeader /> <GiftCardsListTableHeader isCurrencySelected={isCurrencySelected} />
<GiftCardsListTableFooter /> <GiftCardsListTableFooter />
<TableBody> <TableBody>
{renderCollection( {renderCollection(
giftCards, giftCards,
({ giftCard => {
id, const {
displayCode, id,
usedBy, last4CodeChars,
usedByEmail, usedBy,
tag, usedByEmail,
isActive, tags,
product, product,
currentBalance, currentBalance
isExpired } = giftCard;
}) => (
<TableRow return (
onClick={redirectToGiftCardUpdate(id)} <TableRow
className={classes.row} onClick={redirectToGiftCardUpdate(id)}
key={id} className={classes.row}
> key={id}
<TableCell padding="checkbox"> hover={!!giftCard}
<Checkbox >
disableClickPropagation <TableCell padding="checkbox">
checked={isSelected(id)} <Checkbox
onChange={() => toggle(id)} disableClickPropagation
/> checked={isSelected(id)}
</TableCell> onChange={() => toggle(id)}
<TableCell className={classes.colCardCode}> />
<div className={classes.cardCodeContainer}> </TableCell>
<Typography> <TableCell className={classes.colCardCode}>
{intl.formatMessage(messages.codeEndingWithLabel, { <div className={classes.cardCodeContainer}>
displayCode <Typography>
})} {intl.formatMessage(messages.codeEndingWithLabel, {
</Typography> last4CodeChars
<> })}
<HorizontalSpacer spacing={2} /> </Typography>
{selectGiftCardStatusChip({ isActive, isExpired })} <>
</> <HorizontalSpacer spacing={2} />
</div> <GiftCardStatusChip giftCard={giftCard} />
</TableCell> </>
<TableCell> </div>
<Typography>{tag || PLACEHOLDER}</Typography> </TableCell>
</TableCell> <TableCell>
<TableCell> <Typography>{getTagCellText(tags)}</Typography>
{product ? ( </TableCell>
<Link onClick={() => navigate(productUrl(product?.id))}> <TableCell>
{product?.name} {product ? (
</Link> <Link href={productUrl(product?.id)}>
) : ( {product?.name}
PLACEHOLDER </Link>
)} ) : (
</TableCell> PLACEHOLDER
<TableCell> )}
{usedBy ? ( </TableCell>
<Link onClick={() => navigate(customerUrl(usedBy?.id))}> <TableCell>
{`${usedBy?.firstName} ${usedBy?.lastName}`} {usedBy ? (
</Link> <Link href={customerUrl(usedBy?.id)}>
) : ( {`${usedBy?.firstName} ${usedBy?.lastName}`}
<Typography noWrap>{usedByEmail || PLACEHOLDER}</Typography> </Link>
)} ) : (
</TableCell> <Typography noWrap>
<TableCell align="right" className={classes.colBalance}> {usedByEmail || PLACEHOLDER}
<div className={classes.moneyContainer}> </Typography>
<Typography variant="caption"> )}
{currentBalance.currency} </TableCell>
</Typography> <TableCell align="right" className={classes.colBalance}>
<HorizontalSpacer spacing={0.5} /> <div className={classes.moneyContainer}>
<Typography>{currentBalance.amount}</Typography> <Typography variant="caption">
</div> {currentBalance.currency}
</TableCell> </Typography>
<TableCell className={classes.colDelete}> <HorizontalSpacer spacing={0.5} />
<DeleteIconButton <Typography>{currentBalance.amount}</Typography>
onClick={event => { </div>
event.stopPropagation(); </TableCell>
openDeleteDialog(id); <TableCell className={classes.colDelete}>
}} <DeleteIconButton
/> onClick={event => {
</TableCell> event.stopPropagation();
</TableRow> openDeleteDialog(id);
), }}
/>
</TableCell>
</TableRow>
);
},
() => ( () => (
<TableRow> <TableRow>
<TableCell colSpan={numberOfColumns}> <TableCell colSpan={numberOfColumns}>

View file

@ -69,10 +69,18 @@ const BulkEnableDisableSection: React.FC = () => {
.filter(getByIds(ids)) .filter(getByIds(ids))
.some(({ isActive }) => isActive); .some(({ isActive }) => isActive);
const areAllSelectedCardsActive = giftCards
.filter(getByIds(ids))
.every(({ isActive }) => isActive);
const hasAnyDisabledCardsSelected = giftCards const hasAnyDisabledCardsSelected = giftCards
.filter(getByIds(ids)) .filter(getByIds(ids))
.some(({ isActive }) => !isActive); .some(({ isActive }) => !isActive);
const areAllSelectedCardsDisabled = giftCards
.filter(getByIds(ids))
.every(({ isActive }) => !isActive);
const [ const [
activateGiftCards, activateGiftCards,
activateGiftCardsOpts activateGiftCardsOpts
@ -95,24 +103,29 @@ const BulkEnableDisableSection: React.FC = () => {
const handleDeactivateGiftCards = () => const handleDeactivateGiftCards = () =>
deactivateGiftCards({ variables: { ids } }); deactivateGiftCards({ variables: { ids } });
const isSelectionMixed =
hasAnyEnabledCardsSelected && hasAnyDisabledCardsSelected;
return ( return (
<> <>
<ConfirmButton {(areAllSelectedCardsDisabled || isSelectionMixed) && (
disabled={hasAnyEnabledCardsSelected} <ConfirmButton
onClick={handleActivateGiftCards} onClick={handleActivateGiftCards}
variant="text" variant="text"
transitionState={activateGiftCardsOpts?.status} transitionState={activateGiftCardsOpts?.status}
> >
{intl.formatMessage(messages.enableLabel)} {intl.formatMessage(messages.enableLabel)}
</ConfirmButton> </ConfirmButton>
<ConfirmButton )}
disabled={hasAnyDisabledCardsSelected} {(areAllSelectedCardsActive || isSelectionMixed) && (
onClick={handleDeactivateGiftCards} <ConfirmButton
variant="text" onClick={handleDeactivateGiftCards}
transitionState={deactivateGiftCardsOpts?.status} variant="text"
> transitionState={deactivateGiftCardsOpts?.status}
{intl.formatMessage(messages.disableLabel)} >
</ConfirmButton> {intl.formatMessage(messages.disableLabel)}
</ConfirmButton>
)}
</> </>
); );
}; };

View file

@ -1,34 +1,52 @@
import { TableCell } from "@material-ui/core"; import { TableCell } from "@material-ui/core";
import DeleteIconButton from "@saleor/components/DeleteIconButton"; import DeleteIconButton from "@saleor/components/DeleteIconButton";
import TableCellHeader, { import TableCellHeader, {
TableCellHeaderArrowDirection,
TableCellHeaderProps TableCellHeaderProps
} from "@saleor/components/TableCellHeader"; } from "@saleor/components/TableCellHeader";
import TableHead from "@saleor/components/TableHead"; import TableHead from "@saleor/components/TableHead";
import Label, { import Label, {
LabelSizes LabelSizes
} from "@saleor/orders/components/OrderHistory/Label"; } from "@saleor/orders/components/OrderHistory/Label";
import { getArrowDirection } from "@saleor/utils/sort";
import React from "react"; import React from "react";
import { MessageDescriptor, useIntl } from "react-intl"; import { MessageDescriptor, useIntl } from "react-intl";
import { giftCardsListTableMessages as messages } from "../../messages"; import { giftCardsListTableMessages as messages } from "../../messages";
import useGiftCardListDialogs from "../../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs"; import useGiftCardListDialogs from "../../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
import useGiftCardListSort from "../../providers/GiftCardListDialogsProvider/hooks/useGiftCardListSort";
import useGiftCardList from "../../providers/GiftCardListProvider/hooks/useGiftCardList"; import useGiftCardList from "../../providers/GiftCardListProvider/hooks/useGiftCardList";
import useGiftCardListBulkActions from "../../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions"; import useGiftCardListBulkActions from "../../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { canBeSorted } from "../../sort";
import { useTableStyles as useStyles } from "../../styles"; import { useTableStyles as useStyles } from "../../styles";
import { GiftCardUrlSortField } from "../../types";
import BulkEnableDisableSection from "./BulkEnableDisableSection"; import BulkEnableDisableSection from "./BulkEnableDisableSection";
interface HeaderItem { interface HeaderItem {
title?: MessageDescriptor; title?: MessageDescriptor;
options?: TableCellHeaderProps; options?: TableCellHeaderProps;
onClick?: () => void;
direction?: TableCellHeaderArrowDirection;
canBeSorted?: boolean;
} }
const GiftCardsListTableHeader: React.FC = () => { interface GiftCardsListTableHeaderProps {
isCurrencySelected: boolean;
}
const GiftCardsListTableHeader: React.FC<GiftCardsListTableHeaderProps> = ({
isCurrencySelected
}) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
const { giftCards, numberOfColumns, loading } = useGiftCardList(); const { giftCards, numberOfColumns, loading } = useGiftCardList();
const { toggleAll, listElements } = useGiftCardListBulkActions(); const { toggleAll, listElements } = useGiftCardListBulkActions();
const { openDeleteDialog } = useGiftCardListDialogs(); const { openDeleteDialog } = useGiftCardListDialogs();
const { onSort, sort } = useGiftCardListSort();
const getDirection = (sortField: GiftCardUrlSortField) =>
sort.sort === sortField ? getArrowDirection(sort.asc) : undefined;
const headerItems: HeaderItem[] = [ const headerItems: HeaderItem[] = [
{ {
@ -42,17 +60,24 @@ const GiftCardsListTableHeader: React.FC = () => {
title: messages.giftCardsTableColumnTagTitle title: messages.giftCardsTableColumnTagTitle
}, },
{ {
title: messages.giftCardsTableColumnProductTitle title: messages.giftCardsTableColumnProductTitle,
onClick: () => onSort(GiftCardUrlSortField.product),
direction: getDirection(GiftCardUrlSortField.product)
}, },
{ {
title: messages.giftCardsTableColumnCustomerTitle title: messages.giftCardsTableColumnCustomerTitle,
onClick: () => onSort(GiftCardUrlSortField.usedBy),
direction: getDirection(GiftCardUrlSortField.usedBy)
}, },
{ {
title: messages.giftCardsTableColumnBalanceTitle, title: messages.giftCardsTableColumnBalanceTitle,
options: { options: {
className: classes.colBalance, className: classes.colBalance,
textAlign: "right" textAlign: "right"
} },
onClick: () => onSort(GiftCardUrlSortField.balance),
direction: getDirection(GiftCardUrlSortField.balance),
canBeSorted: canBeSorted(GiftCardUrlSortField.balance, isCurrencySelected)
} }
]; ];
@ -76,15 +101,22 @@ const GiftCardsListTableHeader: React.FC = () => {
toolbar={ toolbar={
<> <>
<BulkEnableDisableSection /> <BulkEnableDisableSection />
<DeleteIconButton onClick={openDeleteDialog} /> <DeleteIconButton onClick={() => openDeleteDialog()} />
</> </>
} }
> >
{headerItems.map(({ title, options }) => ( {headerItems.map(
<TableCellHeader {...options}> ({ title, options, onClick, direction, canBeSorted = true }) => (
<Label text={intl.formatMessage(title)} size={LabelSizes.md} /> <TableCellHeader
</TableCellHeader> {...options}
))} onClick={onClick}
disabled={!canBeSorted}
direction={direction}
>
<Label text={intl.formatMessage(title)} size={LabelSizes.md} />
</TableCellHeader>
)
)}
<TableCell className={classes.colDelete} /> <TableCell className={classes.colDelete} />
</TableHead> </TableHead>
</> </>

Some files were not shown because too many files have changed in this diff Show more