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": "GiftCardTag"
},
{
"name": "Group"
},

View file

@ -1966,6 +1966,10 @@
"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}?}}"
},
"src_dot_components_dot_CardMenu_dot_cardMenuItemLoading": {
"context": "menu item loading",
"string": "working..."
},
"src_dot_components_dot_ChannelsAvailabilityCard_dot_3326160357": {
"context": "section header",
"string": "Availability"
@ -3509,215 +3513,336 @@
"context": "gift cards section name",
"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": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label",
"context": "expiry date label",
"string": "Exact date"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryOnLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry on label",
"context": "expires on label",
"string": "Will expire on:"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryPeriodLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry period label",
"context": "expires in label",
"string": "Expires in"
},
"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"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_amountLabel": {
"context": "GiftCardCreateDialog amount label",
"context": "money amount input label",
"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": {
"context": "GiftCardCreateDialog copied to clipboard title",
"context": "copied to clipboard alert title",
"string": "Copied to clipboard"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_copyCodeLabel": {
"context": "GiftCardCreateDialog copy code label",
"context": "copy code button label",
"string": "Copy code"
},
"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:"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_createdSuccessAlertTitle": {
"context": "GiftCardCreateDialog createdSuccessAlertTitle",
"context": "successfully created gift card alert title",
"string": "Successfully created gift card"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_customerLabel": {
"context": "GiftCardCreateDialog customer label",
"context": "customer input label",
"string": "Customer"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_giftCardsAmountLabel": {
"context": "issued cards amount label",
"string": "Cards Issued"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_issueButtonLabel": {
"context": "GiftCardCreateDialog issue button label",
"context": "issue gift card button label",
"string": "Issue"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_noteLabel": {
"context": "GiftCardCreateDialog note label",
"context": "note input label",
"string": "Note"
},
"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"
},
"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."
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationLabel": {
"context": "GiftCarUpdateDetailsExpirySection requires activation label",
"context": "requires activation checkbox label",
"string": "Requires activation"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_title": {
"context": "GiftCardCreateDialog title",
"context": "issue gift card dialog title",
"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": {
"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."
},
"src_dot_giftCards_dot_GiftCardSettings_dot_GiftCardExpirySettingsCard_dot_expiryDateTitle": {
"context": "section header",
"context": "expiry date section header",
"string": "Expiry date"
},
"src_dot_giftCards_dot_GiftCardSettings_dot_title": {
"context": "header",
"context": "gift card settings header",
"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": {
"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"
},
"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:"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_emailInputPlaceholder": {
"context": "GiftCardResendCodeDialog emailInputPlaceholder",
"context": "provided email input placeholder",
"string": "Provided email address"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_sendToChannelSelectLabel": {
"context": "ChannelPickerSelectField sendToChannelLabel",
"context": "send to channel select label",
"string": "Send to channel"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_submitButtonLabel": {
"context": "GiftCardResendCodeDialog submitButtonLabel",
"context": "resend button label",
"string": "Resend"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_successResendAlertText": {
"context": "GiftCardResendCodeDialog successResendAlertText",
"context": "resent code success message",
"string": "Successfully resent code to customer!"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_title": {
"context": "GiftCardResendCodeDialog title",
"context": "resend code to customer title",
"string": "Resend code to customer"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_changeButtonLabel": {
"context": "GiftCardUpdateDetailsCard set balance dialog change button label",
"context": "change button label",
"string": "Change"
},
"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"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_title": {
"context": "GiftCardUpdateDetailsCard set balance button label",
"context": "set balance dialog title label",
"string": "Set balance"
},
"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"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_cardBalanceLabel": {
"context": "GiftCardUpdateDetailsCard card balance label",
"context": "card balance label",
"string": "Card Balance"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_setBalanceButtonLabel": {
"context": "GiftCardUpdateDetailsCard set balance button label",
"context": "set balance button label",
"string": "set balance"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_tagInputLabel": {
"context": "GiftCardTagInput tag label",
"context": "tag label",
"string": "Card Tag"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateDetailsCard_dot_title": {
"context": "GiftCardUpdateDetailsCard title",
"context": "details title",
"string": "Details"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiredOnLabel": {
"context": "GiftCarUpdateDetailsExpirySection expired on label",
"context": "expired on label",
"string": "Expired on {date}"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateCheckboxLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date checkbox label",
"context": "expiry date checkbox label",
"string": "Gift card expires"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label",
"context": "expiration date label",
"string": "Expiration date"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_boughtByLabel": {
"context": "GiftCardUpdateInfoCard bought by label",
"context": "bought by label",
"string": "Bought by"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_creationLabel": {
"context": "GiftCardUpdateInfoCard creation label",
"context": "creation label",
"string": "Creation"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_issuedByAppLabel": {
"context": "GiftCardUpdateInfoCard issued by app label",
"context": "issued by app label",
"string": "Issued by app"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_issuedByLabel": {
"context": "GiftCardUpdateInfoCard issued by label",
"context": "issued by label",
"string": "Issued by"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_orderNumberLabel": {
"context": "GiftCardUpdateInfoCard order number label",
"context": "order number label",
"string": "Order number"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_productLabel": {
"context": "GiftCardUpdateInfoCard product label",
"context": "product label",
"string": "Product bought to get gift card"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_title": {
"context": "GiftCardUpdateInfoCard title",
"context": "info card title",
"string": "Card information"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_usedByLabel": {
"context": "GiftCardUpdateInfoCard used by label",
"context": "used by label",
"string": "Used by"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_disabledStatusLabel": {
"context": "giftCardUpdatePageHeader disabled status label",
"context": "disabled status label",
"string": "Disabled"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_expiredStatusLabel": {
"context": "giftCardUpdatePageHeader expired status label",
"context": "expired status label",
"string": "Expired"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_resendButtonLabel": {
"context": "giftCardUpdatePageHeader resendButtonLabel",
"context": "resend code label",
"string": "Resend code"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyDisabledTitle": {
"context": "GiftCardEnableDisableSection disable success",
"context": "success gift card disable message",
"string": "Successfully disabled gift card"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyEnabledTitle": {
"context": "GiftCardEnableDisableSection enable success",
"context": "success gift card enable message",
"string": "Successfully enabled gift card"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_notFound": {
"context": "giftCardErrorMessages not found",
"context": "gift card not found message",
"string": "Couldn't find gift card"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_title": {
"context": "GiftCardUpdateDetailsCard title",
"context": "title",
"string": "Details"
},
"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"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_balanceAmountLabel": {
@ -3725,7 +3850,7 @@
"string": "Amount"
},
"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"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_currencyLabel": {
@ -3737,7 +3862,7 @@
"string": "Current balance"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_defaultTabLabel": {
"context": "gift card default tab label",
"context": "all gift cards label",
"string": "All Gift Cards"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_disabledOptionLabel": {
@ -3757,7 +3882,7 @@
"string": "Product"
},
"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}"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardListSearchAndFilters_dot_statusLabel": {
@ -3772,136 +3897,172 @@
"context": "used by filter label",
"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": {
"context": "GiftCardEnableDisableSection enable label",
"context": "bulk disable label",
"string": "Deactivate"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_enableLabel": {
"context": "GiftCardEnableDisableSection enable label",
"context": "bulk activate label",
"string": "Activate"
},
"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}}"
},
"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}}"
},
"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}}"
},
"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}}"
},
"src_dot_giftCards_dot_GiftCardsList_dot_bulkIssue": {
"context": "GiftCardsListHeader menu item settings",
"context": "bulk issue menu item",
"string": "Bulk Issue"
},
"src_dot_giftCards_dot_GiftCardsList_dot_codeEndingWithLabel": {
"context": "GiftCardsListTable code ending with label",
"string": "Code ending with {displayCode}"
"context": "code ending with label",
"string": "Code ending with {last4CodeChars}"
},
"src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProduct": {
"context": "GiftCardsListHeader alert",
"context": "create gift card product alert message",
"string": "Create a gift card product"
},
"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"
},
"src_dot_giftCards_dot_GiftCardsList_dot_exportCodes": {
"context": "GiftCardsListHeader menu item settings",
"context": "export card codes menu item",
"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": {
"context": "GiftCardsListHeader alert",
"context": "gift card product message",
"string": "gift card product"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnBalanceTitle": {
"context": "GiftCardsListTable column title balance",
"context": "column title balance",
"string": "Balance"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnCustomerTitle": {
"context": "GiftCardsListTable column title customer",
"context": "column title used by/customer",
"string": "Used by"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnGiftCardTitle": {
"context": "GiftCardsListTable column title gift card",
"context": "column title gift card",
"string": "Gift Card"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnProductTitle": {
"context": "GiftCardsListTable column title product",
"context": "column title product",
"string": "Product"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnTagTitle": {
"context": "GiftCardsListTable column title tag",
"context": "column title tag",
"string": "Tag"
},
"src_dot_giftCards_dot_GiftCardsList_dot_issueButtonLabel": {
"context": "GiftCardsListHeader issue button label",
"context": "issue card button label",
"string": "Issue card"
},
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsAlertTitle": {
"context": "GiftCardsListHeader alert",
"context": "no card defuned alert message",
"string": "You havent defined a gift card product!"
},
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsFound": {
"context": "GiftCardsListTable no cards found title",
"context": "no cards found title message",
"string": "No gift cards found"
},
"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."
},
"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."
},
"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."
},
"src_dot_giftCards_dot_GiftCardsList_dot_settings": {
"context": "GiftCardsListHeader menu item settings",
"context": "settings menu item",
"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": {
"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}}"
},
"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?}}"
},
"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}}"
},
"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}}"
},
"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?}}"
},
"src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_channelSelectLabel": {
"context": "GiftCardCreateDialog channel select label",
"context": "channel select label",
"string": "Channel"
},
"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"
},
"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."
},
"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"
},
"src_dot_giftCards_dot_components_dot_GiftCardSettingsExpirySelect_dot_setExpirationPeriodDescription": {
@ -3913,23 +4074,23 @@
"string": "Set gift card expiration period"
},
"src_dot_giftCards_dot_components_dot_GiftCardTagInput_dot_placeholder": {
"context": "GiftCardTagInput tag placeholder",
"context": "input placeholder tag",
"string": "Tag"
},
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_dayLabel": {
"context": "TimePeriodTextWithSelectField day label",
"context": "days after label",
"string": "days after issue"
},
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_monthLabel": {
"context": "TimePeriodTextWithSelectField month label",
"context": "months after label",
"string": "months after issue"
},
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_weekLabel": {
"context": "TimePeriodTextWithSelectField day label",
"context": "weeks after label",
"string": "weeks after issue"
},
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_yearLabel": {
"context": "TimePeriodTextWithSelectField year label",
"context": "years after label",
"string": "years after issue"
},
"src_dot_home": {
@ -4659,6 +4820,18 @@
"context": "order",
"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": {
"context": "order",
"string": "Created"
@ -5790,19 +5963,15 @@
},
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionGiftCardDescription": {
"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": {
"context": "option",
"string": "Products of this type can be used as gift card/voucher"
},
"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"
"string": "Gift card product type"
},
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionNormalTitle": {
"context": "option",
"string": "This is a normal product"
"string": "Regular product type"
},
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_productTypeName": {
"context": "label",
@ -5985,15 +6154,15 @@
"string": "SEO Information"
},
"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})"
},
"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:"
},
"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})"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2474350154": {
@ -6005,17 +6174,9 @@
"string": "Information exported"
},
"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:"
},
"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": {
"context": "product export to csv file, header",
"string": "Export Settings"
@ -6025,7 +6186,7 @@
"string": "Select All"
},
"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."
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3599582104": {
@ -6039,7 +6200,7 @@
"string": "Inventory Information"
},
"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"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_472026385": {
@ -6051,13 +6212,25 @@
"string": "Search by attribute name"
},
"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})"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_734825715": {
"context": "informations about product prices etc, header",
"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": {
"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."
@ -6104,6 +6277,10 @@
"context": "product is 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": {
"context": "product status",
"string": "Out Of Stock"
@ -6716,6 +6893,14 @@
"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"
},
"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": {
"string": "Manage Products Channel Availability"
},

17
package-lock.json generated
View file

@ -6413,23 +6413,6 @@
"requires": {
"@types/react": "*",
"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": {

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

@ -31,7 +31,13 @@ const Story: React.FC<Partial<
SingleAutocompleteSelectFieldProps & {
enableLoadMore: boolean;
}
>> = ({ allowCustomValues, emptyOption, enableLoadMore, nakedInput }) => {
>> = ({
allowCustomValues,
emptyOption,
enableLoadMore,
nakedInput,
disabled
}) => {
const [displayValue, setDisplayValue] = React.useState(suggestions[0].label);
return (
@ -60,6 +66,7 @@ const Story: React.FC<Partial<
allowCustomValues={allowCustomValues}
emptyOption={emptyOption}
nakedInput={nakedInput}
disabled={disabled}
/>
);
}}
@ -106,9 +113,13 @@ storiesOf("Generics / Select with autocomplete", module)
<SingleAutocompleteSelectFieldContent {...contentProps} choices={[]} />
))
.add("naked", () => <Story nakedInput />)
.add("naked and disabled", () => <Story nakedInput disabled />)
.add("interactive", () => <Story />)
.add("interactive with custom option", () => (
<Story allowCustomValues={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,
id: undefined,
onBlur: handleBlur,
onClick: toggleMenu,
onClick: !disabled && toggleMenu,
onFocus: () => {
if (fetchOnFocus) {
fetchChoices(inputValue);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,6 +8,7 @@ import { MetadataFormData } from "@saleor/components/Metadata/types";
import PageHeader from "@saleor/components/PageHeader";
import Savebar from "@saleor/components/Savebar";
import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment";
import CustomerGiftCardsCard from "@saleor/giftCards/components/GiftCardCustomerCard/CustomerGiftCardsCard";
import { SubmitPromise } from "@saleor/hooks/useForm";
import { sectionNames } from "@saleor/intl";
import { Backlink } from "@saleor/macaw-ui";
@ -117,6 +118,8 @@ const CustomerDetailsPage: React.FC<CustomerDetailsPageProps> = ({
/>
<CardSpacer />
<CustomerStats customer={customer} />
<CardSpacer />
<CustomerGiftCardsCard />
</div>
</Grid>
<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,
CustomerDetailsVariables
>(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 useNotifier from "@saleor/hooks/useNotifier";
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 { FormattedMessage, useIntl } from "react-intl";
import { useIntl } from "react-intl";
import { orderListUrl, orderUrl } from "../../orders/urls";
import CustomerDetailsPage, {
CustomerDetailsPageFormData
} from "../components/CustomerDetailsPage/CustomerDetailsPage";
import {
TypedRemoveCustomerMutation,
TypedUpdateCustomerMutation
} from "../mutations";
import { TypedCustomerDetailsQuery } from "../queries";
import { CustomerDetailsProvider } from "../providers/CustomerDetailsProvider";
import { RemoveCustomer } from "../types/RemoveCustomer";
import { UpdateCustomer } from "../types/UpdateCustomer";
import {
customerAddressesUrl,
customerListUrl,
customerUrl,
CustomerUrlQueryParams
} from "../urls";
import { customerListUrl, CustomerUrlQueryParams } from "../urls";
import { CustomerDetailsContent } from "./CustomerDetailsContent";
interface CustomerDetailsViewProps {
id: string;
@ -75,109 +57,18 @@ export const CustomerDetailsView: React.FC<CustomerDetailsViewProps> = ({
{(removeCustomer, removeCustomerOpts) => (
<TypedUpdateCustomerMutation onCompleted={handleCustomerUpdateSuccess}>
{(updateCustomer, updateCustomerOpts) => (
<TypedCustomerDetailsQuery displayLoader variables={{ id }}>
{customerDetails => {
const user = customerDetails.data?.user;
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={
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
})
)
}
<CustomerDetailsProvider id={id}>
<CustomerDetailsContent
handleBack={handleBack}
id={id}
navigate={navigate}
params={params}
removeCustomer={removeCustomer}
removeCustomerOpts={removeCustomerOpts}
updateCustomer={updateCustomer}
updateCustomerOpts={updateCustomerOpts}
/>
<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>
</CustomerDetailsProvider>
)}
</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",
height: "100%",
width: "100%",
padding: theme.spacing
padding: theme.spacing(3)
}
}),
{ 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 { useIntl } from "react-intl";
import { giftCardCreateDialogMessages as messages } from "./messages";
import { giftCardCreateMessages as messages } from "./messages";
import { useGiftCardCreateDialogCodeContentStyles as useStyles } from "./styles";
interface GiftCardCreateDialogCodeContentProps {

View file

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

View file

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

View file

@ -5,12 +5,14 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import RadioGroupField from "@saleor/components/RadioGroupField";
import TimePeriodField from "@saleor/giftCards/components/TimePeriodField";
import {
GiftCardCreateFormCommonProps,
GiftCardExpiryType
} from "@saleor/giftCards/GiftCardCreateDialog/types";
GiftCardBulkCreateFormErrors,
GiftCardCreateCommonFormData
} from "@saleor/giftCards/GiftCardBulkCreateDialog/types";
import { GiftCardExpiryType } from "@saleor/giftCards/GiftCardCreateDialog/types";
import { getExpiryPeriodTerminationDate } from "@saleor/giftCards/GiftCardCreateDialog/utils";
import { getGiftCardErrorMessage } from "@saleor/giftCards/GiftCardUpdate/messages";
import useCurrentDate from "@saleor/hooks/useCurrentDate";
import { FormChange } from "@saleor/hooks/useForm";
import React from "react";
import { FormattedMessage } 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,
change,
data: {

View file

@ -3,18 +3,18 @@ import { defineMessages } from "react-intl";
export const giftCardCreateExpirySelectMessages = defineMessages({
expirySelectedLabel: {
defaultMessage: "Set gift card expiry date",
description: "GiftCarUpdateDetailsExpirySection expiry selected label"
description: "set expiry date selected label"
},
expiryPeriodLabel: {
defaultMessage: "Expires in",
description: "GiftCarUpdateDetailsExpirySection expiry period label"
description: "expires in label"
},
expiryDateLabel: {
defaultMessage: "Exact date",
description: "GiftCarUpdateDetailsExpirySection expiry date label"
description: "expiry date label"
},
expiryOnLabel: {
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 { ChangeEvent } from "@saleor/hooks/useForm";
import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
import useLocalStorage from "@saleor/hooks/useLocalStorage";
import { mapSingleValueNodeToChoice } from "@saleor/utils/maps";
import * as React from "react";
import { useEffect } from "react";
import { useIntl } from "react-intl";
import {
GiftCardBulkCreateFormErrors,
GiftCardCreateCommonFormData
} from "../GiftCardBulkCreateDialog/types";
import { getGiftCardErrorMessage } from "../GiftCardUpdate/messages";
import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm";
import { giftCardCreateDialogMessages as messages } from "./messages";
import { giftCardCreateMessages as messages } from "./messages";
import { useChannelCurrencies } from "./queries";
import { useGiftCardCreateDialogFormStyles as useStyles } from "./styles";
import { GiftCardCreateFormCommonProps } from "./types";
import { useGiftCardCreateFormStyles as useStyles } from "./styles";
interface GiftCardCreateDialogMoneyInputProps
extends GiftCardCreateFormCommonProps {
set: (data: Partial<GiftCardCreateFormData>) => void;
interface GiftCardCreateMoneyInputProps {
change: FormChange;
errors: GiftCardBulkCreateFormErrors;
data: Pick<GiftCardCreateCommonFormData, "balanceCurrency" | "balanceAmount">;
set: (data: Partial<GiftCardCreateCommonFormData>) => void;
}
const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputProps> = ({
const GiftCardCreateMoneyInput: React.FC<GiftCardCreateMoneyInputProps> = ({
errors,
data: { balanceAmount, balanceCurrency },
change,
@ -32,7 +36,7 @@ const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputPro
const { channelCurrencies } = channelCurrenciesData?.shop;
const [savedCurrency, setCurrency] = useLocalStorage(
"giftCardCreateDialogCurrency",
"giftCardCreateCurrency",
undefined
);
@ -67,7 +71,7 @@ const GiftCardCreateDialogMoneyInput: React.FC<GiftCardCreateDialogMoneyInputPro
helperText={getGiftCardErrorMessage(errors?.balance, intl)}
change={handleInputChange}
choices={mapSingleValueNodeToChoice(channelCurrencies)}
containerClassName={classes.balanceContainer}
containerClassName={classes.fullWidthContainer}
textFieldProps={{
type: "float",
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 { useIntl } from "react-intl";
import { giftCardCreateDialogMessages as messages } from "./messages";
import { giftCardCreateMessages as messages } from "./messages";
import { GiftCardCreateFormCustomer } from "./types";
export interface GiftCardCustomerSelectFieldProps {
selectedCustomer: GiftCardCreateFormCustomer;
setSelectedCustomer: (customer: GiftCardCreateFormCustomer) => void;
disabled?: boolean;
}
const GiftCardCustomerSelectField: React.FC<GiftCardCustomerSelectFieldProps> = ({
selectedCustomer,
setSelectedCustomer
setSelectedCustomer,
disabled = false
}) => {
const intl = useIntl();
@ -54,6 +56,7 @@ const GiftCardCustomerSelectField: React.FC<GiftCardCustomerSelectFieldProps> =
fetchChoices={search}
onChange={handleSelect}
onFetchMore={loadMore}
disabled={disabled}
/>
);
};

View file

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

View file

@ -1,53 +1,79 @@
import { defineMessages } from "react-intl";
export const giftCardCreateDialogMessages = defineMessages({
export const giftCardCreateMessages = defineMessages({
title: {
defaultMessage: "Issue gift card",
description: "GiftCardCreateDialog title"
description: "issue gift card dialog title"
},
amountLabel: {
defaultMessage: "Enter amount",
description: "GiftCardCreateDialog amount label"
description: "money amount input label"
},
issueButtonLabel: {
defaultMessage: "Issue",
description: "GiftCardCreateDialog issue button label"
description: "issue gift card button label"
},
customerLabel: {
defaultMessage: "Customer",
description: "GiftCardCreateDialog customer label"
description: "customer input label"
},
noteLabel: {
defaultMessage: "Note",
description: "GiftCardCreateDialog note label"
description: "note input label"
},
noteSubtitle: {
defaultMessage:
"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: {
defaultMessage: "This is the code of a created gift card:",
description: "GiftCardCreateDialog created gift card label"
description: "created gift card code label"
},
copyCodeLabel: {
defaultMessage: "Copy code",
description: "GiftCardCreateDialog copy code label"
description: "copy code button label"
},
copiedToClipboardTitle: {
defaultMessage: "Copied to clipboard",
description: "GiftCardCreateDialog copied to clipboard title"
description: "copied to clipboard alert title"
},
createdSuccessAlertTitle: {
defaultMessage: "Successfully created gift card",
description: "GiftCardCreateDialog createdSuccessAlertTitle"
description: "successfully created gift card alert title"
},
requiresActivationLabel: {
defaultMessage: "Requires activation",
description: "GiftCarUpdateDetailsExpirySection requires activation label"
description: "requires activation checkbox label"
},
requiresActivationCaption: {
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" }
);
export const useGiftCardCreateDialogFormStyles = makeStyles(
export const useGiftCardCreateFormStyles = makeStyles(
() => ({
noteField: {
width: "100%"
@ -19,7 +19,12 @@ export const useGiftCardCreateDialogFormStyles = makeStyles(
currencySelectField: {
width: 100
},
balanceContainer: { width: "100%" }
fullWidthContainer: { width: "100%" },
dialogContent: {
minWidth: 550,
overflowY: "auto",
overflowX: "hidden"
}
}),
{ name: "GiftCardCreateDialogForm" }
);

View file

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

View file

@ -1,7 +1,13 @@
import { IMessage } from "@saleor/components/messages";
import { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
import commonErrorMessages from "@saleor/utils/errors/common";
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 = (
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 = (
{
expirySelected,
@ -35,7 +74,7 @@ export const getGiftCardExpiryInputData = (
expiryDate,
expiryPeriodAmount,
expiryPeriodType
}: GiftCardCreateFormData,
}: GiftCardCreateCommonFormData,
currentDate: number
): string => {
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({
expiryDateSectionDescription: {
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: {
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({
title: {
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 useNotifier from "@saleor/hooks/useNotifier";
import { getBySlug } from "@saleor/products/components/ProductVariantCreatorPage/utils";
import { DialogProps } from "@saleor/types";
import commonErrorMessages from "@saleor/utils/errors/common";
import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers";
import { mapSlugNodeToChoice } from "@saleor/utils/maps";
import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl";
@ -29,10 +29,7 @@ export interface GiftCardResendCodeFormData {
channelSlug: string;
}
const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({
open,
closeDialog
}) => {
const GiftCardResendCodeDialog: React.FC<DialogProps> = ({ open, onClose }) => {
const intl = useIntl();
const notify = useNotifier();
const classes = useStyles();
@ -99,7 +96,7 @@ const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({
notify(notifierData);
if (!errors.length) {
closeDialog();
onClose();
reset();
}
};
@ -128,7 +125,7 @@ const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({
open={open}
onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.submitButtonLabel)}
onClose={closeDialog}
onClose={onClose}
title={intl.formatMessage(messages.title)}
confirmButtonState={status}
disabled={loading}

View file

@ -3,31 +3,31 @@ import { defineMessages } from "react-intl";
export const giftCardResendCodeDialogMessages = defineMessages({
title: {
defaultMessage: "Resend code to customer",
description: "GiftCardResendCodeDialog title"
description: "resend code to customer title"
},
description: {
defaultMessage:
"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: {
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: {
defaultMessage: "Resend",
description: "GiftCardResendCodeDialog submitButtonLabel"
description: "resend button label"
},
emailInputPlaceholder: {
defaultMessage: "Provided email address",
description: "GiftCardResendCodeDialog emailInputPlaceholder"
description: "provided email input placeholder"
},
successResendAlertText: {
defaultMessage: "Successfully resent code to customer!",
description: "GiftCardResendCodeDialog successResendAlertText"
description: "resent code success message"
},
sendToChannelSelectLabel: {
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;
}
export interface GiftCardResend_giftCardResend_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardResend_giftCardResend_giftCard {
__typename: "GiftCard";
metadata: (GiftCardResend_giftCardResend_giftCard_metadata | null)[];
privateMetadata: (GiftCardResend_giftCardResend_giftCard_privateMetadata | null)[];
displayCode: string;
last4CodeChars: string;
boughtInChannel: string | null;
createdBy: GiftCardResend_giftCardResend_giftCard_createdBy | null;
product: GiftCardResend_giftCardResend_giftCard_product | null;
@ -84,7 +89,7 @@ export interface GiftCardResend_giftCardResend_giftCard {
initialBalance: GiftCardResend_giftCardResend_giftCard_initialBalance | null;
currentBalance: GiftCardResend_giftCardResend_giftCard_currentBalance | null;
id: string;
tag: string | null;
tags: GiftCardResend_giftCardResend_giftCard_tags[] | null;
}
export interface GiftCardResend_giftCardResend {

View file

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

View file

@ -3,20 +3,19 @@ import { defineMessages } from "react-intl";
export const giftCardUpdateBalanceDialogMessages = defineMessages({
title: {
defaultMessage: "Set balance",
description: "GiftCardUpdateDetailsCard set balance button label"
description: "set balance dialog title label"
},
subtitle: {
defaultMessage:
"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: {
defaultMessage: "Successfully updated card balance",
description: "GiftCardUpdateDetailsCard update success alert title"
description: "card update success alert title"
},
changeButtonLabel: {
defaultMessage: "Change",
description:
"GiftCardUpdateDetailsCard set balance dialog change button label"
description: "change button label"
}
});

View file

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

View file

@ -3,18 +3,18 @@ import { defineMessages } from "react-intl";
export const giftCardUpdateDetailsCardMessages = defineMessages({
title: {
defaultMessage: "Details",
description: "GiftCardUpdateDetailsCard title"
description: "details title"
},
setBalanceButtonLabel: {
defaultMessage: "set balance",
description: "GiftCardUpdateDetailsCard set balance button label"
description: "set balance button label"
},
cardBalanceLabel: {
defaultMessage: "Card Balance",
description: "GiftCardUpdateDetailsCard card balance label"
description: "card balance label"
},
tagInputLabel: {
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({
expiryDateCheckboxLabel: {
defaultMessage: "Gift card expires",
description: "GiftCarUpdateDetailsExpirySection expiry date checkbox label"
description: "expiry date checkbox label"
},
expiryDateLabel: {
defaultMessage: "Expiration date",
description: "GiftCarUpdateDetailsExpirySection expiry date label"
description: "expiration date label"
},
expiredOnLabel: {
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 { customerUrl } from "@saleor/customers/urls";
import useDateLocalize from "@saleor/hooks/useDateLocalize";
import useNavigator from "@saleor/hooks/useNavigator";
import { getFullName, getStringOrPlaceholder } from "@saleor/misc";
import Label from "@saleor/orders/components/OrderHistory/Label";
import { getOrderNumberLinkObject } from "@saleor/orders/components/OrderHistory/utils";
@ -22,7 +21,6 @@ import { giftCardUpdateInfoCardMessages as messages } from "./messages";
const GiftCardUpdateInfoCardContent: React.FC = () => {
const intl = useIntl();
const localizeDate = useDateLocalize();
const navigate = useNavigator();
const { giftCard } = useGiftCardDetails();
@ -122,7 +120,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(messages.orderNumberLabel)} />
{orderData ? (
<Link onClick={() => navigate(orderData.link)}>{orderData.text}</Link>
<Link href={orderData.link}>{orderData.text}</Link>
) : (
<Typography>{PLACEHOLDER}</Typography>
)}
@ -130,9 +128,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(messages.productLabel)} />
{product ? (
<Link onClick={() => navigate(productUrl(product?.id))}>
{product?.name}
</Link>
<Link href={productUrl(product?.id)}>{product?.name}</Link>
) : (
<Typography>{PLACEHOLDER}</Typography>
)}
@ -140,7 +136,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(buyerLabelMessage)} />
{buyerUrl ? (
<Link onClick={() => navigate(buyerUrl)}>{buyerName}</Link>
<Link href={buyerUrl}>{buyerName}</Link>
) : (
<Typography>{buyerName}</Typography>
)}
@ -148,9 +144,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
<Label text={intl.formatMessage(messages.usedByLabel)} />
{usedBy ? (
<Link onClick={() => navigate(customerUrl(usedBy.id))}>
{getFullName(usedBy)}
</Link>
<Link href={customerUrl(usedBy.id)}>{getFullName(usedBy)}</Link>
) : (
<Typography>
{getStringOrPlaceholder(usedByEmail, PLACEHOLDER)}

View file

@ -3,34 +3,34 @@ import { defineMessages } from "react-intl";
export const giftCardUpdateInfoCardMessages = defineMessages({
title: {
defaultMessage: "Card information",
description: "GiftCardUpdateInfoCard title"
description: "info card title"
},
creationLabel: {
defaultMessage: "Creation",
description: "GiftCardUpdateInfoCard creation label"
description: "creation label"
},
orderNumberLabel: {
defaultMessage: "Order number",
description: "GiftCardUpdateInfoCard order number label"
description: "order number label"
},
productLabel: {
defaultMessage: "Product bought to get gift card",
description: "GiftCardUpdateInfoCard product label"
description: "product label"
},
issuedByLabel: {
defaultMessage: "Issued by",
description: "GiftCardUpdateInfoCard issued by label"
description: "issued by label"
},
issuedByAppLabel: {
defaultMessage: "Issued by app",
description: "GiftCardUpdateInfoCard issued by app label"
description: "issued by app label"
},
boughtByLabel: {
defaultMessage: "Bought by",
description: "GiftCardUpdateInfoCard bought by label"
description: "bought by label"
},
usedByLabel: {
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 React from "react";
import GiftCardHistory from "./GiftCardHistory/GiftCardHistory";
import GiftCardUpdateDetailsCard from "./GiftCardUpdateDetailsCard";
import GiftCardUpdateInfoCard from "./GiftCardUpdateInfoCard";
import GiftCardUpdatePageHeader from "./GiftCardUpdatePageHeader";
@ -38,6 +39,7 @@ const GiftCardUpdatePage: React.FC = () => {
<div>
<GiftCardUpdateInfoCard />
</div>
<GiftCardHistory />
</Grid>
<Savebar
state={status}

View file

@ -1,22 +1,13 @@
import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl";
import { ConfirmButton } from "@saleor/macaw-ui";
import commonErrorMessages from "@saleor/utils/errors/common";
import React from "react";
import { useIntl } from "react-intl";
import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages";
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
import { giftCardEnableDisableSectionMessages as messages } from "./messages";
import {
useGiftCardActivateMutation,
useGiftCardDeactivateMutation
} from "./mutations";
import { GiftCardActivate } from "./types/GiftCardActivate";
import { GiftCardDeactivate } from "./types/GiftCardDeactivate";
import useGiftCardActivateToggle from "./hooks/useGiftCardActivateToggle";
const GiftCardEnableDisableSection: React.FC = () => {
const notify = useNotifier();
const intl = useIntl();
const {
@ -27,50 +18,12 @@ const GiftCardEnableDisableSection: React.FC = () => {
return null;
}
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)
});
};
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 [
const {
giftCardActivate,
giftCardDeactivate,
giftCardDeactivateOpts
] = useGiftCardDeactivateMutation({
onCompleted: onDeactivateCompleted
currentOpts
} = useGiftCardActivateToggle({
isActive
});
const handleClick = () =>
@ -82,8 +35,6 @@ const GiftCardEnableDisableSection: React.FC = () => {
? buttonMessages.disableLabel
: buttonMessages.enableLabel;
const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts;
return (
<ConfirmButton
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 PageHeader from "@saleor/components/PageHeader";
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 { Backlink } from "@saleor/macaw-ui";
import React from "react";
@ -25,42 +25,25 @@ const GiftCardUpdatePageHeader: React.FC = () => {
const { openResendCodeDialog } = useGiftCardUpdateDialogs();
const { displayCode, isActive, isExpired } = giftCard;
const { last4CodeChars, isExpired } = giftCard;
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 (
<>
<Backlink onClick={navigateBack}>
{intl.formatMessage(sectionNames.giftCards)}
</Backlink>
<PageHeader inline title={getPageTitle()}>
<PageHeader
inline
title={
<PageTitleWithStatusChip title={title}>
<GiftCardStatusChip giftCard={giftCard} />
</PageTitleWithStatusChip>
}
>
<GiftCardEnableDisableSection />
<HorizontalSpacer />
{!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({
successfullyEnabledTitle: {
defaultMessage: "Successfully enabled gift card",
description: "GiftCardEnableDisableSection enable success"
description: "success gift card enable message"
},
successfullyDisabledTitle: {
defaultMessage: "Successfully disabled gift card",
description: "GiftCardEnableDisableSection disable success"
description: "success gift card disable message"
}
});
export const giftCardUpdatePageHeaderMessages = defineMessages({
resendButtonLabel: {
defaultMessage: "Resend code",
description: "giftCardUpdatePageHeader resendButtonLabel"
description: "resend code label"
},
expiredStatusLabel: {
defaultMessage: "Expired",
description: "giftCardUpdatePageHeader expired status label"
description: "expired status label"
},
disabledStatusLabel: {
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;
}
export interface GiftCardActivate_giftCardActivate_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardActivate_giftCardActivate_giftCard {
__typename: "GiftCard";
metadata: (GiftCardActivate_giftCardActivate_giftCard_metadata | null)[];
privateMetadata: (GiftCardActivate_giftCardActivate_giftCard_privateMetadata | null)[];
displayCode: string;
last4CodeChars: string;
boughtInChannel: string | null;
createdBy: GiftCardActivate_giftCardActivate_giftCard_createdBy | null;
product: GiftCardActivate_giftCardActivate_giftCard_product | null;
@ -84,7 +89,7 @@ export interface GiftCardActivate_giftCardActivate_giftCard {
initialBalance: GiftCardActivate_giftCardActivate_giftCard_initialBalance | null;
currentBalance: GiftCardActivate_giftCardActivate_giftCard_currentBalance | null;
id: string;
tag: string | null;
tags: GiftCardActivate_giftCardActivate_giftCard_tags[] | null;
}
export interface GiftCardActivate_giftCardActivate {

View file

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

View file

@ -1,19 +1,21 @@
import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
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";
export const giftCardUpdateDetailsCardMessages = defineMessages({
title: {
defaultMessage: "Details",
description: "GiftCardUpdateDetailsCard title"
description: "title"
}
});
const giftCardErrorMessages = defineMessages({
notFound: {
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) {
case GiftCardErrorCode.NOT_FOUND:
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 gql from "graphql-tag";
import { giftCardDataFragment } from "./queries";
import { giftCardDataFragment, giftCardEventsFragment } from "./queries";
import {
GiftCardAddNote,
GiftCardAddNoteVariables
} from "./types/GiftCardAddNote";
import {
GiftCardUpdate,
GiftCardUpdateVariables
@ -11,6 +15,7 @@ import {
const giftCardUpdate = gql`
${giftCardDataFragment}
${giftCardErrorFragment}
${giftCardEventsFragment}
mutation GiftCardUpdate($id: ID!, $input: GiftCardUpdateInput!) {
giftCardUpdate(id: $id, input: $input) {
errors {
@ -18,6 +23,9 @@ const giftCardUpdate = gql`
}
giftCard {
...GiftCardData
events {
...GiftCardEvent
}
}
}
}
@ -27,3 +35,28 @@ export const useGiftCardUpdateMutation = makeMutation<
GiftCardUpdate,
GiftCardUpdateVariables
>(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 {
navigateBack: () => void;
closeDialog: () => void;
onClose: () => void;
openSetBalanceDialog: () => void;
openDeleteDialog: () => void;
openResendCodeDialog: () => void;
@ -45,7 +45,7 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
RESEND_CODE
} = GiftCardUpdatePageActionParamsEnum;
const [openDialog, closeDialog] = createDialogActionHandlers<
const [openDialog, onClose] = createDialogActionHandlers<
GiftCardUpdatePageActionParamsEnum,
GiftCardUpdatePageUrlQueryParams
>(navigate, params => giftCardUrl(id, params), params);
@ -59,7 +59,7 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
openSetBalanceDialog: () => openDialog(SET_BALANCE),
openDeleteDialog: () => openDialog(DELETE),
openResendCodeDialog: () => openDialog(RESEND_CODE),
closeDialog,
onClose,
navigateBack
};
@ -69,17 +69,17 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
{!loadingGiftCard && (
<>
<GiftCardUpdateBalanceDialog
closeDialog={closeDialog}
onClose={onClose}
open={isDialogOpen(SET_BALANCE)}
/>
<GiftCardUpdatePageDeleteDialog
closeDialog={closeDialog}
onClose={onClose}
open={isDialogOpen(DELETE)}
navigateBack={navigateBack}
/>
<GiftCardResendCodeDialog
open={isDialogOpen(RESEND_CODE)}
closeDialog={closeDialog}
onClose={onClose}
/>
</>
)}

View file

@ -1,5 +1,6 @@
import { MetadataFormData } from "@saleor/components/Metadata";
import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { giftCardUpdateFormMessages } from "@saleor/giftCards/GiftCardsList/messages";
import { MutationResultWithOpts } from "@saleor/hooks/makeMutation";
import useForm, { FormChange, UseFormResult } from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier";
@ -14,6 +15,7 @@ import {
usePrivateMetadataUpdate
} from "@saleor/utils/metadata/updateMetadata";
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
import difference from "lodash/difference";
import React, { createContext } from "react";
import { useIntl } from "react-intl";
@ -30,7 +32,7 @@ interface GiftCardUpdateFormProviderProps {
}
export type GiftCardUpdateFormData = MetadataFormData &
Pick<GiftCardCreateFormData, "tag" | "expiryDate">;
Pick<GiftCardCreateFormData, "tags" | "expiryDate">;
export interface GiftCardUpdateFormConsumerData
extends GiftCardUpdateFormErrors {
@ -38,7 +40,7 @@ export interface GiftCardUpdateFormConsumerData
}
export interface GiftCardUpdateFormErrors {
formErrors: Record<"tag" | "expiryDate", GiftCardError>;
formErrors: Record<"tags" | "expiryDate", GiftCardError>;
handlers: { changeMetadata: FormChange };
}
@ -51,6 +53,19 @@ export const GiftCardUpdateFormContext = createContext<
GiftCardUpdateFormConsumerProps
>(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> = ({
children
}) => {
@ -66,10 +81,10 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
return { ...emptyFormData, metadata: [], privateMetadata: [] };
}
const { tag, expiryDate, privateMetadata, metadata } = giftCard;
const { tags, expiryDate, privateMetadata, metadata } = giftCard;
return {
tag,
tags: tags.map(({ name }) => name),
expiryDate,
privateMetadata: privateMetadata?.map(mapMetadataItemToInput),
metadata: metadata?.map(mapMetadataItemToInput)
@ -78,21 +93,37 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
const onSubmit = (data: GiftCardUpdate) => {
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({
onCompleted: onSubmit
});
const submit = async ({ tag, expiryDate }: GiftCardUpdateFormData) => {
const submit = async ({ tags, expiryDate }: GiftCardUpdateFormData) => {
const result = await updateGiftCard({
variables: {
id: giftCard?.id,
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);
const formErrors = getFormErrors(
["tag", "expiryDate"],
["tags", "expiryDate"],
updateGiftCardOpts?.data?.giftCardUpdate?.errors
);

View file

@ -15,7 +15,7 @@ export const giftCardDataFragment = gql`
${fragmentUserBase}
fragment GiftCardData on GiftCard {
...MetadataFragment
displayCode
last4CodeChars
boughtInChannel
createdBy {
...UserBase
@ -48,7 +48,9 @@ export const giftCardDataFragment = gql`
}
id
tag
tags {
name
}
}
`;
@ -65,17 +67,17 @@ export const giftCardDetails = gql`
type
user {
...UserBase
email
}
app {
id
name
}
message
email
orderId
orderNumber
tag
oldTag
tags
oldTags
balance {
initialBalance {
...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<
GiftCardDetails,
GiftCardDetailsVariables
>(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;
}
export interface GiftCardData_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardData {
__typename: "GiftCard";
metadata: (GiftCardData_metadata | null)[];
privateMetadata: (GiftCardData_privateMetadata | null)[];
displayCode: string;
last4CodeChars: string;
boughtInChannel: string | null;
createdBy: GiftCardData_createdBy | null;
product: GiftCardData_product | null;
@ -76,5 +81,5 @@ export interface GiftCardData {
initialBalance: GiftCardData_initialBalance | null;
currentBalance: GiftCardData_currentBalance | null;
id: string;
tag: string | null;
tags: GiftCardData_tags[] | null;
}

View file

@ -59,11 +59,17 @@ export interface GiftCardDetails_giftCard_currentBalance {
currency: string;
}
export interface GiftCardDetails_giftCard_tags {
__typename: "GiftCardTag";
name: string;
}
export interface GiftCardDetails_giftCard_events_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
email: string;
}
export interface GiftCardDetails_giftCard_events_app {
@ -114,11 +120,10 @@ export interface GiftCardDetails_giftCard_events {
user: GiftCardDetails_giftCard_events_user | null;
app: GiftCardDetails_giftCard_events_app | null;
message: string | null;
email: string | null;
orderId: string | null;
orderNumber: string | null;
tag: string | null;
oldTag: string | null;
tags: string[] | null;
oldTags: string[] | null;
balance: GiftCardDetails_giftCard_events_balance | null;
}
@ -126,7 +131,7 @@ export interface GiftCardDetails_giftCard {
__typename: "GiftCard";
metadata: (GiftCardDetails_giftCard_metadata | null)[];
privateMetadata: (GiftCardDetails_giftCard_privateMetadata | null)[];
displayCode: string;
last4CodeChars: string;
boughtInChannel: string | null;
createdBy: GiftCardDetails_giftCard_createdBy | null;
product: GiftCardDetails_giftCard_product | null;
@ -141,7 +146,7 @@ export interface GiftCardDetails_giftCard {
initialBalance: GiftCardDetails_giftCard_initialBalance | null;
currentBalance: GiftCardDetails_giftCard_currentBalance | null;
id: string;
tag: string | null;
tags: GiftCardDetails_giftCard_tags[] | null;
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
// 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
@ -65,11 +65,79 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard_currentBalance {
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 {
__typename: "GiftCard";
metadata: (GiftCardUpdate_giftCardUpdate_giftCard_metadata | null)[];
privateMetadata: (GiftCardUpdate_giftCardUpdate_giftCard_privateMetadata | null)[];
displayCode: string;
last4CodeChars: string;
boughtInChannel: string | null;
createdBy: GiftCardUpdate_giftCardUpdate_giftCard_createdBy | null;
product: GiftCardUpdate_giftCardUpdate_giftCard_product | null;
@ -84,7 +152,8 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard {
initialBalance: GiftCardUpdate_giftCardUpdate_giftCard_initialBalance | null;
currentBalance: GiftCardUpdate_giftCardUpdate_giftCard_currentBalance | null;
id: string;
tag: string | null;
tags: GiftCardUpdate_giftCardUpdate_giftCard_tags[] | null;
events: GiftCardUpdate_giftCardUpdate_giftCard_events[];
}
export interface GiftCardUpdate_giftCardUpdate {

View file

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

View file

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

View file

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

View file

@ -3,21 +3,21 @@ import { defineMessages } from "react-intl";
export const giftCardListFilterErrorMessages = defineMessages({
balanceAmount: {
defaultMessage: "Balance amount is missing",
description: "Filter balance amount error"
description: "balance amound missing error message"
},
balanceCurrency: {
defaultMessage: "Balance currency is missing",
description: "Filter balance currency error"
description: "balance curreny missing error message"
}
});
export const giftCardListSearchAndFiltersMessages = defineMessages({
searchPlaceholder: {
defaultMessage: "Search Gift Cards, e.g {exampleGiftCardCode}",
description: "gift card search placeholder"
description: "search gift card placeholder"
},
defaultTabLabel: {
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 navigate = useNavigator();
const { openCreateDialog } = useGiftCardListDialogs();
const {
openCreateDialog,
openBulkCreateDialog,
openExportDialog
} = useGiftCardListDialogs();
const openSettings = () => navigate(giftCardSettingsUrl);
@ -26,17 +30,17 @@ const GiftCardsListHeader: React.FC = () => {
label: intl.formatMessage(messages.settings),
testId: "settingsMenuItem",
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 (

View file

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

View file

@ -1,4 +1,7 @@
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 { FormattedMessage } from "react-intl";
@ -8,28 +11,27 @@ import { useHeaderStyles as useStyles } from "../styles";
interface GiftCardsListHeaderAlertContentProps {
giftCardProductTypesExist: boolean;
giftCardProductsExist: boolean;
handleCreateGiftCardProductType: () => void;
handleCreateGiftCardProduct: () => void;
}
const GiftCardsListHeaderAlertContent: React.FC<GiftCardsListHeaderAlertContentProps> = ({
giftCardProductTypesExist,
giftCardProductsExist,
handleCreateGiftCardProductType,
handleCreateGiftCardProduct
giftCardProductsExist
}) => {
const classes = useStyles({});
const giftCardProductTypeUrl = productTypeAddUrl({
kind: ProductTypeKindEnum.GIFT_CARD
});
const giftCardCreateGiftCardProductUrl = productAddUrl();
if (!giftCardProductTypesExist) {
return (
<FormattedMessage
{...messages.noGiftCardsProductTypes}
values={{
createGiftCardProductType: (
<Link
onClick={handleCreateGiftCardProductType}
className={classes.alertLink}
>
<Link href={giftCardProductTypeUrl} className={classes.alertLink}>
<FormattedMessage {...messages.createGiftCardProductType} />
</Link>
)
@ -45,7 +47,7 @@ const GiftCardsListHeaderAlertContent: React.FC<GiftCardsListHeaderAlertContentP
values={{
createGiftCardProduct: (
<Link
onClick={handleCreateGiftCardProduct}
href={giftCardCreateGiftCardProductUrl}
className={classes.alertLink}
>
<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,95 +11,80 @@ import DeleteIconButton from "@saleor/components/DeleteIconButton";
import Link from "@saleor/components/Link";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
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 GiftCardStatusChip from "@saleor/giftCards/components/GiftCardStatusChip/GiftCardStatusChip";
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 { renderCollection } from "@saleor/misc";
import { productUrl } from "@saleor/products/urls";
import React from "react";
import { useEffect } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { giftCardUpdatePageHeaderMessages as giftCardStatusChipMessages } from "../../GiftCardUpdate/GiftCardUpdatePageHeader/messages";
import GiftCardListSearchAndFilters from "../GiftCardListSearchAndFilters";
import { giftCardsListTableMessages as messages } from "../messages";
import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
import useGiftCardListBulkActions from "../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { canBeSorted } from "../sort";
import { useTableStyles as useStyles } from "../styles";
import { GiftCardUrlSortField } from "../types";
import GiftCardsListTableFooter from "./GiftCardsListTableFooter";
import GiftCardsListTableHeader from "./GiftCardsListTableHeader";
import { getTagCellText } from "./utils";
const GiftCardsListTable: React.FC = () => {
const intl = useIntl();
const classes = useStyles({});
const navigate = useNavigator();
const { giftCards, numberOfColumns, loading } = useGiftCardList();
const { giftCards, numberOfColumns, loading, params } = useGiftCardList();
const { toggle, isSelected } = useGiftCardListBulkActions();
const { openDeleteDialog } = useGiftCardListDialogs();
const isCurrencySelected = !!params.currency;
useEffect(() => {
if (!canBeSorted(params.sort, isCurrencySelected)) {
navigate(
giftCardListUrl({
...params,
sort: GiftCardUrlSortField.usedBy
})
);
}
});
const redirectToGiftCardUpdate = (id: string) => () =>
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 (
<Card>
<GiftCardListSearchAndFilters />
<ResponsiveTable>
<GiftCardsListTableHeader />
<GiftCardsListTableHeader isCurrencySelected={isCurrencySelected} />
<GiftCardsListTableFooter />
<TableBody>
{renderCollection(
giftCards,
({
giftCard => {
const {
id,
displayCode,
last4CodeChars,
usedBy,
usedByEmail,
tag,
isActive,
tags,
product,
currentBalance,
isExpired
}) => (
currentBalance
} = giftCard;
return (
<TableRow
onClick={redirectToGiftCardUpdate(id)}
className={classes.row}
key={id}
hover={!!giftCard}
>
<TableCell padding="checkbox">
<Checkbox
@ -112,21 +97,21 @@ const GiftCardsListTable: React.FC = () => {
<div className={classes.cardCodeContainer}>
<Typography>
{intl.formatMessage(messages.codeEndingWithLabel, {
displayCode
last4CodeChars
})}
</Typography>
<>
<HorizontalSpacer spacing={2} />
{selectGiftCardStatusChip({ isActive, isExpired })}
<GiftCardStatusChip giftCard={giftCard} />
</>
</div>
</TableCell>
<TableCell>
<Typography>{tag || PLACEHOLDER}</Typography>
<Typography>{getTagCellText(tags)}</Typography>
</TableCell>
<TableCell>
{product ? (
<Link onClick={() => navigate(productUrl(product?.id))}>
<Link href={productUrl(product?.id)}>
{product?.name}
</Link>
) : (
@ -135,11 +120,13 @@ const GiftCardsListTable: React.FC = () => {
</TableCell>
<TableCell>
{usedBy ? (
<Link onClick={() => navigate(customerUrl(usedBy?.id))}>
<Link href={customerUrl(usedBy?.id)}>
{`${usedBy?.firstName} ${usedBy?.lastName}`}
</Link>
) : (
<Typography noWrap>{usedByEmail || PLACEHOLDER}</Typography>
<Typography noWrap>
{usedByEmail || PLACEHOLDER}
</Typography>
)}
</TableCell>
<TableCell align="right" className={classes.colBalance}>
@ -160,7 +147,8 @@ const GiftCardsListTable: React.FC = () => {
/>
</TableCell>
</TableRow>
),
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>

View file

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

View file

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

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