Feature - Gift Cards (#1366)

* Add deletion to gift cards (#1298)

* Add gift cards section to menu and add empty list component

* Update messages

* Change styling of app wide page header to match design

* Add gift cards list table wip

* Update prop name for status chip component to make it more consistent with other components

* Replace old trash icon with new one

* Add Size type based on action dialog sizes to be used app wide

* Add delete icon button

* Add new sizes option to status chip component

* Add / update gift cards list components

* Add bulk actions type

* Work on gift cards list WIP

* Small refactor

* Fix styling of gift cards table

* Remove temp files

* Remove unnecessary type

* Add gift cards section to menu and add empty list component

* Update schema and types

* Add link to gift card update page to gift cards list and add route to gift cards index

* Extract order page title with status chip into a separate generic component and use it in order page title

* wip

* Update money component

* Add gift card details card balance section

* Refactor gift card details

* Add vertical spacer component

* Update schema and types

* Add gift card tag input component along with necessary queries

* Add gift card tag input to gift card update page

* Add gift card update details card expiry section WIP

* Add time period select field WIP

* Post rebase refactor

* Add time period select field to gift card update view

* Update schema, types and gift cards query

* Add getFullName util function and replace existing manual usages

* Add text with select field component

* Add gift card update info card and refactor

* Fixes after review

* Fix import

* Add displaying order link in gift card update

* Refactor

* Connect gift card list to api

* refactor

* Add gift card create dialog

* Fix gift card list styles, change location for gift card list query, minor refactor

* Fix menu structure data for gift cards

* Add channel currencies type to shop

* Refactor text with select field

* Add gift card expiry select component

* Add gift card error type and fragment

* Update global types

* Add default prop to getFormErrors function

* Move gift card details provider to providers dir

* Update global utils with mapSingleValueNodeToChoice function

* Update gift card tag input

* Move and refactor time period field

* Update schema

* move format money function to other money ulities

* Update gift card urls

* Add content or skeleton component

* Add gift card create util for extracting expiry settings input data

* Remove content or skeleton component and move displaying logic to existing skeleton

* Move displaying logic of gift card create dialog to list

* Refactor

* Add hooks for gift card bulk actions and gift card list to be used instead of context directly

* Fix types for text with select field + add parsing for number typed field

* Add initial currency to gift card create form

* Fix gift card create dialog closing animation

* Add gift card update info card

* Refactor gift card update details card

* Add gift card balance dialog

* Move gift card update form providers to providers dir

* Connect gift card update page to api, add necessary contexts etc.

* Refactor

* Refactor

* Add hooks to use instead of gift card contexts directly

* Fix types

* Fix text field target name missing in passed event in text with select field

* Add minimal value option to text with select field, add to gift card inputs

* Fix gift card update balance dialog not changing hasChanged prop after submit

* Refactor

* Add enable / disable section to gift card update

* Refactor

* Refactor

* Fix update balance dialog crashing the app when enetered wrong amount

* Fix gift card list table header styles

* Refactor

* Add metadata to gift card update

* Update messages ids

* Refactor

* Refactor

* Refactor

* Refactor

* Update schema and types, add gift card delete and bulk delete mutations

* Fix url change after gift card delete

* Refactor

* Update messages

* Change gift card list providers order

* Refactor/fix after merge

* Add gift card resend mutation and update types

* Add use dialog form reset hook and make update balance dialog use it, to fix it displaying form errors after modal close and open

* Add gift card resend code dialog

* Add button to open gift card resend code dialog

* Update messages

* Add gift card list bulk enable disable section

* Add refetching gift card list query after bulk activate / deactivate, refactor

* Refactor

* Refactor

* Update messages

* Update schema

* Change gift card update expiry settings

* Refactor

* Add gift card settings view (#1300)

* Add gift card settings view

* Refactor

* Create gift card sttings expiry select

* Update test snapshots

* Update schema

* Update gift card settings page title

* Refactor to match rest of the gift cards flow (#1308)

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

* Refactor

* Refactor

* Set common error codes in string union

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

* Add gift card kind to product type (#1307)

* Add gift card kind to product type

* Fix radio label alignment

* Update test snapshots

* Refactor

* Set default kind in product type create

* Change styling of activate / deactivate button in gift card update

* Add week option to time period select field

* Update messages

* Change labels and update messages

* Add auto fulfill non shippable gift cards to order settings

* Fix fixture

* Update messages

* Update snapshots

* Refactor

* Update types

* Add gift card used in order money amount in order payment section

* Remove unnecessary schema changes

* Change types

* Update fixtures and messages

* Refactor

* Add top card to gift card list (#1327)

* Add top card to gift card  list

* Update no gift card products notification messages

* Use alert instead of notification

* Update prodduct and product  type counts

* Return null if none of the conditions pass

* Update messages

* Fix unused product type deletion

* Update types

* Update imports

* Update messages

* Update test snapshots

* Change gift card dialog expiry settings

* Update form of create gift card dialog

* Add activation option to create gift card dialog

* Update event handling

* Update test snapshots

* Use date context provider for moment time

* Update types

* Refactor

* Update messages

* Update snapshots

* Add channel picker to gift card create modal

* Fix styling

* Add channel picker to gift card resend code dialog

* Update schema and types

* Refactor

* Update messages

* Refactor

* Update messages and snapshots

* Fix order gift card money amount (#1371)

* Update messages and snapshots

* Fix order gift card money in payment card when multiple gift cards used

* Fix missing order id check in gift card events in order gift card money used util

* Fix channel selection in gift card create dialog

* Fix order number not showing in gift card update view

* Add gift card settings to gift card create dialog (#1372)

* Fix gift cards number inputs to accept floats

* Fix order number showing as null in gift card update info card

* Fix channel slug adding in gift card create dialog

* Fix gift card list header top card links to display correctly

* Remove unused imports

Co-authored-by: Dawid Tarasiuk <tarasiukdawid@gmail.com>
This commit is contained in:
Magdalena Markusik 2021-09-14 15:57:02 +02:00 committed by GitHub
parent 9b3d90e9c7
commit aaa0a9f309
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
205 changed files with 6354 additions and 1172 deletions

View file

@ -3407,6 +3407,22 @@
"context": "gift cards section name", "context": "gift cards section name",
"string": "Gift Cards" "string": "Gift Cards"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryDateLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label",
"string": "Exact date"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryOnLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry on label",
"string": "Will expire on:"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expiryPeriodLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry period label",
"string": "Expires in"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_GiftCardCreateExpirySelect_dot_expirySelectedLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry selected label",
"string": "Set gift card expiry date"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_amountLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_amountLabel": {
"context": "GiftCardCreateDialog amount label", "context": "GiftCardCreateDialog amount label",
"string": "Enter amount" "string": "Enter amount"
@ -3431,10 +3447,6 @@
"context": "GiftCardCreateDialog customer label", "context": "GiftCardCreateDialog customer label",
"string": "Customer" "string": "Customer"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_customerSubtitle": {
"context": "GiftCardCreateDialog customer 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_GiftCardCreateDialog_dot_issueButtonLabel": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_issueButtonLabel": {
"context": "GiftCardCreateDialog issue button label", "context": "GiftCardCreateDialog issue button label",
"string": "Issue" "string": "Issue"
@ -3447,10 +3459,57 @@
"context": "GiftCardCreateDialog note subtitle", "context": "GiftCardCreateDialog note subtitle",
"string": "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history" "string": "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history"
}, },
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationCaption": {
"context": "GiftCarUpdateDetailsExpirySection requires activation caption",
"string": "All issued cards require activation by staff before use."
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_requiresActivationLabel": {
"context": "GiftCarUpdateDetailsExpirySection requires activation label",
"string": "Requires activation"
},
"src_dot_giftCards_dot_GiftCardCreateDialog_dot_title": { "src_dot_giftCards_dot_GiftCardCreateDialog_dot_title": {
"context": "GiftCardCreateDialog title", "context": "GiftCardCreateDialog title",
"string": "Issue gift card" "string": "Issue gift card"
}, },
"src_dot_giftCards_dot_GiftCardSettings_dot_GiftCardExpirySettingsCard_dot_expiryDateSectionDescription": {
"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",
"string": "Expiry date"
},
"src_dot_giftCards_dot_GiftCardSettings_dot_title": {
"context": "header",
"string": "Gift Cards Settings"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_consentCheckboxLabel": {
"context": "GiftCardResendCodeDialog consentCheckboxLabel",
"string": "Yes, I want to send gift card to different address"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_description": {
"context": "GiftCardResendCodeDialog 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",
"string": "Provided email address"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_sendToChannelSelectLabel": {
"context": "ChannelPickerSelectField sendToChannelLabel",
"string": "Send to channel"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_submitButtonLabel": {
"context": "GiftCardResendCodeDialog submitButtonLabel",
"string": "Resend"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_successResendAlertText": {
"context": "GiftCardResendCodeDialog successResendAlertText",
"string": "Successfully resent code to customer!"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardResendCodeDialog_dot_title": {
"context": "GiftCardResendCodeDialog title",
"string": "Resend code to customer"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_changeButtonLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateBalanceDialog_dot_changeButtonLabel": {
"context": "GiftCardUpdateDetailsCard set balance dialog change button label", "context": "GiftCardUpdateDetailsCard set balance dialog change button label",
"string": "Change" "string": "Change"
@ -3479,6 +3538,14 @@
"context": "GiftCardUpdateDetailsCard title", "context": "GiftCardUpdateDetailsCard title",
"string": "Details" "string": "Details"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateCheckboxLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date checkbox label",
"string": "Gift card expires"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateExpirySelect_dot_expiryDateLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label",
"string": "Expiration date"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_boughtByLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdateInfoCard_dot_boughtByLabel": {
"context": "GiftCardUpdateInfoCard bought by label", "context": "GiftCardUpdateInfoCard bought by label",
"string": "Bought by" "string": "Bought by"
@ -3511,13 +3578,9 @@
"context": "GiftCardUpdateInfoCard used by label", "context": "GiftCardUpdateInfoCard used by label",
"string": "Used by" "string": "Used by"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_disableLabel": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_resendButtonLabel": {
"context": "GiftCardEnableDisableSection enable label", "context": "giftCardUpdatePageHeader resendButtonLabel",
"string": "Disable" "string": "Resend code"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_enableLabel": {
"context": "GiftCardEnableDisableSection enable label",
"string": "Enable"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyDisabledTitle": { "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyDisabledTitle": {
"context": "GiftCardEnableDisableSection disable success", "context": "GiftCardEnableDisableSection disable success",
@ -3527,10 +3590,38 @@
"context": "GiftCardEnableDisableSection enable success", "context": "GiftCardEnableDisableSection enable success",
"string": "Successfully enabled gift card" "string": "Successfully enabled gift card"
}, },
"src_dot_giftCards_dot_GiftCardUpdate_dot_notFound": {
"context": "giftCardErrorMessages not found",
"string": "Couldn't find gift card"
},
"src_dot_giftCards_dot_GiftCardUpdate_dot_title": { "src_dot_giftCards_dot_GiftCardUpdate_dot_title": {
"context": "GiftCardUpdateDetailsCard title", "context": "GiftCardUpdateDetailsCard title",
"string": "Details" "string": "Details"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_disableLabel": {
"context": "GiftCardEnableDisableSection enable label",
"string": "Deactivate"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_enableLabel": {
"context": "GiftCardEnableDisableSection enable label",
"string": "Activate"
},
"src_dot_giftCards_dot_GiftCardsList_dot_GiftCardsListTable_dot_GiftCardsListTableHeader_dot_errorActivateAlertText": {
"context": "GiftCardEnableDisableSection error activate alert text",
"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",
"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",
"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",
"string": "Successfully deactivated gift {count,plural,one{card} other{cards}}"
},
"src_dot_giftCards_dot_GiftCardsList_dot_bulkIssue": { "src_dot_giftCards_dot_GiftCardsList_dot_bulkIssue": {
"context": "GiftCardsListHeader menu item settings", "context": "GiftCardsListHeader menu item settings",
"string": "Bulk Issue" "string": "Bulk Issue"
@ -3539,6 +3630,14 @@
"context": "GiftCardsListTable code ending with label", "context": "GiftCardsListTable code ending with label",
"string": "Code ending with {displayCode}" "string": "Code ending with {displayCode}"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProduct": {
"context": "GiftCardsListHeader alert",
"string": "Create a gift card product"
},
"src_dot_giftCards_dot_GiftCardsList_dot_createGiftCardProductType": {
"context": "GiftCardsListHeader alert",
"string": "Create a gift card product type"
},
"src_dot_giftCards_dot_GiftCardsList_dot_exportCodes": { "src_dot_giftCards_dot_GiftCardsList_dot_exportCodes": {
"context": "GiftCardsListHeader menu item settings", "context": "GiftCardsListHeader menu item settings",
"string": "Export card codes" "string": "Export card codes"
@ -3547,6 +3646,10 @@
"context": "GiftCardsListTable disabled label", "context": "GiftCardsListTable disabled label",
"string": "Disabled" "string": "Disabled"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardProduct": {
"context": "GiftCardsListHeader alert",
"string": "gift card product"
},
"src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnBalanceTitle": { "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnBalanceTitle": {
"context": "GiftCardsListTable column title balance", "context": "GiftCardsListTable column title balance",
"string": "Balance" "string": "Balance"
@ -3571,29 +3674,73 @@
"context": "GiftCardsListHeader issue button label", "context": "GiftCardsListHeader issue button label",
"string": "Issue card" "string": "Issue card"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsAlertTitle": {
"context": "GiftCardsListHeader alert",
"string": "You havent defined a gift card product!"
},
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsFound": { "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsFound": {
"context": "GiftCardsListTable no cards found title", "context": "GiftCardsListTable no cards found title",
"string": "No gift cards found" "string": "No gift cards found"
}, },
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProductTypes": {
"context": "GiftCardsListHeader alert",
"string": "{createGiftCardProductType} to start selling gift cards in your store."
},
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProducts": {
"context": "GiftCardsListHeader alert",
"string": "{createGiftCardProduct} to start selling gift cards in your store."
},
"src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsProductsAndProductTypes": {
"context": "GiftCardsListHeader alert",
"string": "{createGiftCardProductType} and {giftCardProduct} to start selling gift cards in your store."
},
"src_dot_giftCards_dot_GiftCardsList_dot_settings": { "src_dot_giftCards_dot_GiftCardsList_dot_settings": {
"context": "GiftCardsListHeader menu item settings", "context": "GiftCardsListHeader menu item settings",
"string": "Settings" "string": "Settings"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardExpirySelect_dot_expirationDateLabel": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_consentLabel": {
"context": "GiftCarUpdateDetailsExpirySection expiration date label", "context": "GiftCardDeleteDialog consent label",
"string": "Expiration date" "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_GiftCardExpirySelect_dot_expiryDateLabel": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_defaultDescription": {
"context": "GiftCarUpdateDetailsExpirySection expiry date label", "context": "GiftCardDeleteDialog default description",
"string": "Expiration date" "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_GiftCardExpirySelect_dot_expiryPeriodLabel": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_deleteSuccessAlertText": {
"context": "GiftCarUpdateDetailsExpirySection expiry period label", "context": "GiftCardDeleteDialog success alert text",
"string": "Expiry period" "string": "{selectedItemsCount,plural,one{Successfully deleted gift card} other{Successfully deleted gift cards}}"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardExpirySelect_dot_neverExpireLabel": { "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_title": {
"context": "GiftCarUpdateDetailsExpirySection never expire label", "context": "GiftCardDeleteDialog single title",
"string": "Never expire" "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",
"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",
"string": "Channel"
},
"src_dot_giftCards_dot_components_dot_GiftCardSendToCustomer_dot_customerChannelSubtitle": {
"context": "GiftCardCreateDialog 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",
"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",
"string": "Send gift card to customer"
},
"src_dot_giftCards_dot_components_dot_GiftCardSettingsExpirySelect_dot_setExpirationPeriodDescription": {
"context": "checkbox label description",
"string": "Expiration date will be automatically set, once gift card is issued"
},
"src_dot_giftCards_dot_components_dot_GiftCardSettingsExpirySelect_dot_setExpirationPeriodTitle": {
"context": "checkbox label",
"string": "Set gift card expiration period"
}, },
"src_dot_giftCards_dot_components_dot_GiftCardTagInput_dot_label": { "src_dot_giftCards_dot_components_dot_GiftCardTagInput_dot_label": {
"context": "GiftCardTagInput tag label", "context": "GiftCardTagInput tag label",
@ -3605,15 +3752,19 @@
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_dayLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_dayLabel": {
"context": "TimePeriodTextWithSelectField day label", "context": "TimePeriodTextWithSelectField day label",
"string": "days after usage" "string": "days after issue"
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_monthLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_monthLabel": {
"context": "TimePeriodTextWithSelectField month label", "context": "TimePeriodTextWithSelectField month label",
"string": "months after usage" "string": "months after issue"
},
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_weekLabel": {
"context": "TimePeriodTextWithSelectField day label",
"string": "weeks after issue"
}, },
"src_dot_giftCards_dot_components_dot_TimePeriodField_dot_yearLabel": { "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_yearLabel": {
"context": "TimePeriodTextWithSelectField year label", "context": "TimePeriodTextWithSelectField year label",
"string": "years after usage" "string": "years after issue"
}, },
"src_dot_home": { "src_dot_home": {
"context": "home section name", "context": "home section name",
@ -4411,6 +4562,10 @@
"context": "order discount", "context": "order discount",
"string": "Discount" "string": "Discount"
}, },
"src_dot_orders_dot_components_dot_OrderPayment_dot_372187363": {
"context": "order payment",
"string": "Paid with Gift Card"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_3768782744": { "src_dot_orders_dot_components_dot_OrderPayment_dot_3768782744": {
"context": "order payment", "context": "order payment",
"string": "Preauthorized amount" "string": "Preauthorized amount"
@ -4752,6 +4907,14 @@
"context": "section header", "context": "section header",
"string": "Settings" "string": "Settings"
}, },
"src_dot_orders_dot_components_dot_OrderSettings_dot_2116402092": {
"context": "checkbox gift cards label",
"string": "Automatically fulfill non shippable gift cards"
},
"src_dot_orders_dot_components_dot_OrderSettings_dot_2879928595": {
"context": "checkbox gift cards label description",
"string": "when activated non-shippable gift cards will be automatically set as fulfilled and sent to customer"
},
"src_dot_orders_dot_components_dot_OrderSettings_dot_3281882935": { "src_dot_orders_dot_components_dot_OrderSettings_dot_3281882935": {
"context": "checkbox label description", "context": "checkbox label description",
"string": "All orders will be automatically confirmed and all payments will be captured." "string": "All orders will be automatically confirmed and all payments will be captured."
@ -5402,7 +5565,24 @@
"context": "switch button", "context": "switch button",
"string": "Product type uses Variant Attributes" "string": "Product type uses Variant Attributes"
}, },
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_1007996279": { "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"
},
"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"
},
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_optionNormalTitle": {
"context": "option",
"string": "This is a normal product"
},
"src_dot_productTypes_dot_components_dot_ProductTypeDetails_dot_productTypeName": {
"context": "label",
"string": "Product Type Name" "string": "Product Type Name"
}, },
"src_dot_productTypes_dot_components_dot_ProductTypeListPage_dot_1776073799": { "src_dot_productTypes_dot_components_dot_ProductTypeListPage_dot_1776073799": {
@ -7594,6 +7774,10 @@
"context": "event", "context": "event",
"string": "Product Variant out of stock" "string": "Product Variant out of stock"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2315238863": {
"context": "event",
"string": "Product variant out of stock"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2454751033": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2454751033": {
"context": "event", "context": "event",
"string": "All events" "string": "All events"
@ -7690,6 +7874,10 @@
"context": "event", "context": "event",
"string": "Page created" "string": "Page created"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_69923590": {
"context": "event",
"string": "Product variant back in stock"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_759562905": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_759562905": {
"context": "event", "context": "event",
"string": "Authorize payment" "string": "Authorize payment"

View file

@ -1111,6 +1111,7 @@ enum CheckoutErrorCode {
TAX_ERROR TAX_ERROR
UNIQUE UNIQUE
VOUCHER_NOT_APPLICABLE VOUCHER_NOT_APPLICABLE
GIFT_CARD_NOT_APPLICABLE
ZERO_QUANTITY ZERO_QUANTITY
MISSING_CHANNEL_SLUG MISSING_CHANNEL_SLUG
CHANNEL_INACTIVE CHANNEL_INACTIVE
@ -2253,7 +2254,6 @@ type GiftCard implements Node & ObjectWithMetadata {
code: String! code: String!
isActive: Boolean! isActive: Boolean!
expiryDate: Date expiryDate: Date
expiryType: GiftCardExpiryTypeEnum!
tag: String tag: String
created: DateTime! created: DateTime!
lastUsedOn: DateTime lastUsedOn: DateTime
@ -2268,9 +2268,9 @@ type GiftCard implements Node & ObjectWithMetadata {
createdByEmail: String createdByEmail: String
usedByEmail: String usedByEmail: String
app: App app: App
expiryPeriod: TimePeriod
product: Product product: Product
events: [GiftCardEvent!]! events: [GiftCardEvent!]!
boughtInChannel: String
user: User @deprecated(reason: "This field will be removed in Saleor 4.0. Use `createdBy` field instead.") user: User @deprecated(reason: "This field will be removed in Saleor 4.0. Use `createdBy` field instead.")
endDate: DateTime @deprecated(reason: "This field will be removed in Saleor 4.0. Use `expiryDate` field instead.") endDate: DateTime @deprecated(reason: "This field will be removed in Saleor 4.0. Use `expiryDate` field instead.")
startDate: DateTime @deprecated(reason: "This field will be removed in Saleor 4.0.") startDate: DateTime @deprecated(reason: "This field will be removed in Saleor 4.0.")
@ -2282,6 +2282,31 @@ type GiftCardActivate {
errors: [GiftCardError!]! errors: [GiftCardError!]!
} }
type GiftCardAddNote {
giftCard: GiftCard
event: GiftCardEvent
errors: [GiftCardError!]!
}
input GiftCardAddNoteInput {
message: String!
}
type GiftCardBulkActivate {
count: Int!
errors: [GiftCardError!]!
}
type GiftCardBulkDeactivate {
count: Int!
errors: [GiftCardError!]!
}
type GiftCardBulkDelete {
count: Int!
errors: [GiftCardError!]!
}
type GiftCardCountableConnection { type GiftCardCountableConnection {
pageInfo: PageInfo! pageInfo: PageInfo!
edges: [GiftCardCountableEdge!]! edges: [GiftCardCountableEdge!]!
@ -2301,11 +2326,13 @@ type GiftCardCreate {
input GiftCardCreateInput { input GiftCardCreateInput {
tag: String tag: String
expiryDate: Date
startDate: Date startDate: Date
endDate: Date endDate: Date
balance: PriceInput! balance: PriceInput!
userEmail: String userEmail: String
expirySettings: GiftCardExpirySettingsInput! channel: String
isActive: Boolean!
code: String code: String
note: String note: String
} }
@ -2350,25 +2377,17 @@ type GiftCardEvent implements Node {
tag: String tag: String
oldTag: String oldTag: String
balance: GiftCardEventBalance balance: GiftCardEventBalance
expiry: GiftCardEventExpiry expiryDate: Date
oldExpiryDate: Date
} }
type GiftCardEventBalance { type GiftCardEventBalance {
initialBalance: Money! initialBalance: Money
currentBalance: Money! currentBalance: Money!
oldInitialBalance: Money oldInitialBalance: Money
oldCurrentBalance: Money oldCurrentBalance: Money
} }
type GiftCardEventExpiry {
expiryType: GiftCardExpiryTypeEnum
expiryPeriod: TimePeriod
expiryDate: Date
oldExpiryType: GiftCardExpiryTypeEnum
oldExpiryPeriod: TimePeriod
oldExpiryDate: Date
}
enum GiftCardEventsEnum { enum GiftCardEventsEnum {
ISSUED ISSUED
BOUGHT BOUGHT
@ -2376,25 +2395,77 @@ enum GiftCardEventsEnum {
ACTIVATED ACTIVATED
DEACTIVATED DEACTIVATED
BALANCE_RESET BALANCE_RESET
EXPIRY_SETTINGS_UPDATED EXPIRY_DATE_UPDATED
SENT_TO_CUSTOMER SENT_TO_CUSTOMER
RESENT RESENT
} NOTE_ADDED
USED_IN_ORDER
input GiftCardExpirySettingsInput {
expiryType: GiftCardExpiryTypeEnum!
expiryDate: Date
expiryPeriod: TimePeriodInputType
}
enum GiftCardExpiryTypeEnum {
NEVER_EXPIRE
EXPIRY_PERIOD
EXPIRY_DATE
} }
input GiftCardFilterInput { input GiftCardFilterInput {
isActive: Boolean
tag: String tag: String
tags: [String]
products: [ID]
usedBy: [ID]
currency: String
currentBalance: PriceRangeInput
initialBalance: PriceRangeInput
}
type GiftCardResend {
giftCard: GiftCard
errors: [GiftCardError!]!
}
input GiftCardResendInput {
id: ID!
email: String
channel: String!
}
type GiftCardSettings {
expiryType: GiftCardSettingsExpiryTypeEnum!
expiryPeriod: TimePeriod
}
type GiftCardSettingsError {
field: String
message: String
code: GiftCardSettingsErrorCode!
}
enum GiftCardSettingsErrorCode {
INVALID
REQUIRED
GRAPHQL_ERROR
}
enum GiftCardSettingsExpiryTypeEnum {
NEVER_EXPIRE
EXPIRY_PERIOD
}
type GiftCardSettingsUpdate {
giftCardSettings: GiftCardSettings
errors: [GiftCardSettingsError!]!
}
input GiftCardSettingsUpdateInput {
expiryType: GiftCardSettingsExpiryTypeEnum
expiryPeriod: TimePeriodInputType
}
enum GiftCardSortField {
TAG
PRODUCT
USED_BY
CURRENT_BALANCE
}
input GiftCardSortingInput {
direction: OrderDirection!
field: GiftCardSortField!
} }
type GiftCardUpdate { type GiftCardUpdate {
@ -2405,10 +2476,10 @@ type GiftCardUpdate {
input GiftCardUpdateInput { input GiftCardUpdateInput {
tag: String tag: String
expiryDate: Date
startDate: Date startDate: Date
endDate: Date endDate: Date
balanceAmount: PositiveDecimal balanceAmount: PositiveDecimal
expirySettings: GiftCardExpirySettingsInput
} }
type Group implements Node { type Group implements Node {
@ -3645,6 +3716,7 @@ type Mutation {
shopSettingsTranslate(input: ShopSettingsTranslationInput!, languageCode: LanguageCodeEnum!): ShopSettingsTranslate shopSettingsTranslate(input: ShopSettingsTranslationInput!, languageCode: LanguageCodeEnum!): ShopSettingsTranslate
shopAddressUpdate(input: AddressInput): ShopAddressUpdate shopAddressUpdate(input: AddressInput): ShopAddressUpdate
orderSettingsUpdate(input: OrderSettingsUpdateInput!): OrderSettingsUpdate orderSettingsUpdate(input: OrderSettingsUpdateInput!): OrderSettingsUpdate
giftCardSettingsUpdate(input: GiftCardSettingsUpdateInput!): GiftCardSettingsUpdate
shippingMethodChannelListingUpdate(id: ID!, input: ShippingMethodChannelListingInput!): ShippingMethodChannelListingUpdate shippingMethodChannelListingUpdate(id: ID!, input: ShippingMethodChannelListingInput!): ShippingMethodChannelListingUpdate
shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate
shippingPriceDelete(id: ID!): ShippingPriceDelete shippingPriceDelete(id: ID!): ShippingPriceDelete
@ -3783,6 +3855,11 @@ type Mutation {
giftCardDelete(id: ID!): GiftCardDelete giftCardDelete(id: ID!): GiftCardDelete
giftCardDeactivate(id: ID!): GiftCardDeactivate giftCardDeactivate(id: ID!): GiftCardDeactivate
giftCardUpdate(id: ID!, input: GiftCardUpdateInput!): GiftCardUpdate giftCardUpdate(id: ID!, input: GiftCardUpdateInput!): GiftCardUpdate
giftCardResend(input: GiftCardResendInput!): GiftCardResend
giftCardAddNote(id: ID!, input: GiftCardAddNoteInput!): GiftCardAddNote
giftCardBulkDelete(ids: [ID]!): GiftCardBulkDelete
giftCardBulkActivate(ids: [ID]!): GiftCardBulkActivate
giftCardBulkDeactivate(ids: [ID]!): GiftCardBulkDeactivate
pluginUpdate(channelId: ID, id: ID!, input: PluginUpdateInput!): PluginUpdate pluginUpdate(channelId: ID, id: ID!, input: PluginUpdateInput!): PluginUpdate
externalNotificationTrigger(channel: String!, input: ExternalNotificationTriggerInput!, pluginId: String): ExternalNotificationTrigger externalNotificationTrigger(channel: String!, input: ExternalNotificationTriggerInput!, pluginId: String): ExternalNotificationTrigger
saleCreate(input: SaleInput!): SaleCreate saleCreate(input: SaleInput!): SaleCreate
@ -4383,6 +4460,7 @@ input OrderReturnProductsInput {
type OrderSettings { type OrderSettings {
automaticallyConfirmAllNewOrders: Boolean! automaticallyConfirmAllNewOrders: Boolean!
automaticallyFulfillNonShippableGiftCard: Boolean!
} }
type OrderSettingsError { type OrderSettingsError {
@ -4402,7 +4480,8 @@ type OrderSettingsUpdate {
} }
input OrderSettingsUpdateInput { input OrderSettingsUpdateInput {
automaticallyConfirmAllNewOrders: Boolean! automaticallyConfirmAllNewOrders: Boolean
automaticallyFulfillNonShippableGiftCard: Boolean
} }
enum OrderSortField { enum OrderSortField {
@ -5235,6 +5314,7 @@ input ProductFilterInput {
price: PriceRangeInput price: PriceRangeInput
minimalPrice: PriceRangeInput minimalPrice: PriceRangeInput
productTypes: [ID] productTypes: [ID]
giftCard: Boolean
ids: [ID] ids: [ID]
channel: String channel: String
} }
@ -5397,6 +5477,7 @@ type ProductType implements Node & ObjectWithMetadata {
weight: Weight weight: Weight
privateMetadata: [MetadataItem]! privateMetadata: [MetadataItem]!
metadata: [MetadataItem]! metadata: [MetadataItem]!
kind: ProductTypeKindEnum!
products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection @deprecated(reason: "This field will be removed in Saleor 4.0. Use the top-level `products` query with the `productTypes` filter.") products(channel: String, before: String, after: String, first: Int, last: Int): ProductCountableConnection @deprecated(reason: "This field will be removed in Saleor 4.0. Use the top-level `products` query with the `productTypes` filter.")
taxType: TaxType taxType: TaxType
variantAttributes(variantSelection: VariantAttributeScope): [Attribute] variantAttributes(variantSelection: VariantAttributeScope): [Attribute]
@ -5448,12 +5529,14 @@ input ProductTypeFilterInput {
configurable: ProductTypeConfigurable configurable: ProductTypeConfigurable
productType: ProductTypeEnum productType: ProductTypeEnum
metadata: [MetadataFilter] metadata: [MetadataFilter]
kind: ProductTypeKindEnum
ids: [ID] ids: [ID]
} }
input ProductTypeInput { input ProductTypeInput {
name: String name: String
slug: String slug: String
kind: ProductTypeKindEnum
hasVariants: Boolean hasVariants: Boolean
productAttributes: [ID] productAttributes: [ID]
variantAttributes: [ID] variantAttributes: [ID]
@ -5463,6 +5546,11 @@ input ProductTypeInput {
taxCode: String taxCode: String
} }
enum ProductTypeKindEnum {
NORMAL
GIFT_CARD
}
type ProductTypeReorderAttributes { type ProductTypeReorderAttributes {
productType: ProductType productType: ProductType
productErrors: [ProductError!]! @deprecated(reason: "This field will be removed in Saleor 4.0. Use `errors` field instead.") productErrors: [ProductError!]! @deprecated(reason: "This field will be removed in Saleor 4.0. Use `errors` field instead.")
@ -5682,6 +5770,7 @@ type Query {
stocks(filter: StockFilterInput, before: String, after: String, first: Int, last: Int): StockCountableConnection stocks(filter: StockFilterInput, before: String, after: String, first: Int, last: Int): StockCountableConnection
shop: Shop! shop: Shop!
orderSettings: OrderSettings orderSettings: OrderSettings
giftCardSettings: GiftCardSettings!
shippingZone(id: ID!, channel: String): ShippingZone shippingZone(id: ID!, channel: String): ShippingZone
shippingZones(filter: ShippingZoneFilterInput, channel: String, before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection shippingZones(filter: ShippingZoneFilterInput, channel: String, before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection
digitalContent(id: ID!): DigitalContent digitalContent(id: ID!): DigitalContent
@ -5714,7 +5803,7 @@ type Query {
menuItem(id: ID!, channel: String): MenuItem menuItem(id: ID!, channel: String): MenuItem
menuItems(channel: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection menuItems(channel: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection
giftCard(id: ID!): GiftCard giftCard(id: ID!): GiftCard
giftCards(filter: GiftCardFilterInput, before: String, after: String, first: Int, last: Int): GiftCardCountableConnection giftCards(sortBy: GiftCardSortingInput, filter: GiftCardFilterInput, before: String, after: String, first: Int, last: Int): GiftCardCountableConnection
plugin(id: ID!): Plugin plugin(id: ID!): Plugin
plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection
sale(id: ID!, channel: String): Sale sale(id: ID!, channel: String): Sale
@ -6471,6 +6560,7 @@ input TimePeriodInputType {
enum TimePeriodTypeEnum { enum TimePeriodTypeEnum {
DAY DAY
WEEK
MONTH MONTH
YEAR YEAR
} }

View file

@ -6,7 +6,7 @@ import { ConfirmButtonTransitionState } from "../ConfirmButton";
import DialogButtons from "./DialogButtons"; import DialogButtons from "./DialogButtons";
import { ActionDialogVariant, Size } from "./types"; import { ActionDialogVariant, Size } from "./types";
interface ActionDialogProps extends DialogProps { export interface ActionDialogProps extends DialogProps {
children?: React.ReactNode; children?: React.ReactNode;
confirmButtonLabel?: string; confirmButtonLabel?: string;
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;

View file

@ -57,8 +57,7 @@ export const AppChannelProvider: React.FC = ({ children }) => {
const availableChannels = channelData?.channels || []; const availableChannels = channelData?.channels || [];
const channel = const channel =
channelData && channelData && (availableChannels.find(getById(selectedChannel)) || null);
(availableChannels.find(channel => channel.id === selectedChannel) || null);
return ( return (
<AppChannelContext.Provider <AppChannelContext.Provider

View file

@ -3,13 +3,13 @@ import { ChannelData } from "@saleor/channels/utils";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import RadioSwitchField from "@saleor/components/RadioSwitchField"; import RadioSwitchField from "@saleor/components/RadioSwitchField";
import useCurrentDate from "@saleor/hooks/useCurrentDate";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors"; import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
import classNames from "classnames"; import classNames from "classnames";
import React, { useState } from "react"; import React, { useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { DateContext } from "../../Date/DateContext";
import { useStyles } from "../styles"; import { useStyles } from "../styles";
import { ChannelOpts, ChannelsAvailabilityError, Messages } from "../types"; import { ChannelOpts, ChannelsAvailabilityError, Messages } from "../types";
export interface ChannelContentProps { export interface ChannelContentProps {
@ -43,7 +43,7 @@ const ChannelContent: React.FC<ChannelContentProps> = ({
publicationDate, publicationDate,
...(visibleInListings !== undefined ? { visibleInListings } : {}) ...(visibleInListings !== undefined ? { visibleInListings } : {})
}; };
const dateNow = React.useContext(DateContext); const dateNow = useCurrentDate();
const localizeDate = useDateLocalize(); const localizeDate = useDateLocalize();
const hasAvailableProps = const hasAvailableProps =
isAvailable !== undefined && availableForPurchase !== undefined; isAvailable !== undefined && availableForPurchase !== undefined;

View file

@ -1,3 +1,4 @@
import { Divider } from "@material-ui/core";
import { makeStyles } from "@saleor/macaw-ui"; import { makeStyles } from "@saleor/macaw-ui";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
@ -17,6 +18,9 @@ const useStyles = makeStyles(
} }
} }
}, },
underline: {
marginBottom: theme.spacing(4)
},
grid: { grid: {
padding: theme.spacing(2) padding: theme.spacing(2)
}, },
@ -51,25 +55,34 @@ interface ExtendedPageHeaderProps {
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
inline?: boolean; inline?: boolean;
underline?: boolean;
title?: React.ReactNode; title?: React.ReactNode;
testId?: string; testId?: string;
} }
const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => { const ExtendedPageHeader: React.FC<ExtendedPageHeaderProps> = props => {
const { children, className, inline, title, testId } = props; const { children, className, inline, underline, title, testId } = props;
const classes = useStyles(props); const classes = useStyles(props);
return ( return (
<>
<div <div
data-test-id={testId} data-test-id={testId}
className={classNames(classes.root, className, { className={classNames(classes.root, className, {
[classes.block]: !inline [classes.block]: !inline,
[classes.underline]: underline
})} })}
> >
{title} {title}
<div className={classes.action}>{children}</div> <div className={classes.action}>{children}</div>
</div> </div>
{underline && (
<div className={classes.underline}>
<Divider />
</div>
)}
</>
); );
}; };
ExtendedPageHeader.displayName = "ExtendedPageHeader"; ExtendedPageHeader.displayName = "ExtendedPageHeader";

View file

@ -43,12 +43,13 @@ interface PageHeaderProps {
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
inline?: boolean; inline?: boolean;
underline?: boolean;
limitText?: string; limitText?: string;
title?: React.ReactNode; title?: React.ReactNode;
} }
const PageHeader: React.FC<PageHeaderProps> = props => { const PageHeader: React.FC<PageHeaderProps> = props => {
const { children, className, inline, limitText, title } = props; const { children, className, inline, underline, limitText, title } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -57,6 +58,7 @@ const PageHeader: React.FC<PageHeaderProps> = props => {
testId="page-header" testId="page-header"
className={className} className={className}
inline={inline} inline={inline}
underline={underline}
title={ title={
<Typography className={classes.title} variant="h5"> <Typography className={classes.title} variant="h5">
{title !== undefined ? title : <Skeleton style={{ width: "10em" }} />} {title !== undefined ? title : <Skeleton style={{ width: "10em" }} />}

View file

@ -7,47 +7,11 @@ import {
Radio, Radio,
RadioGroup RadioGroup
} from "@material-ui/core"; } from "@material-ui/core";
import { makeStyles } from "@saleor/macaw-ui";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
const useStyles = makeStyles( import { useStyles } from "./styles";
theme => ({
alignTop: {
alignSelf: "baseline",
position: "relative",
top: -6
},
formLabel: {
marginBottom: theme.spacing(1)
},
radioGroupInline: {
flexDirection: "row"
},
radioLabel: {
marginBottom: theme.spacing(-0.5)
},
radioLabelInline: {
marginRight: theme.spacing(4)
},
root: {
"& $radioLabel": {
"&:last-of-type": {
marginBottom: 0
}
},
padding: 0,
width: "100%"
},
rootNoLabel: {
marginTop: theme.spacing(-1.5)
}
}),
{
name: "RadioGroupField"
}
);
export interface RadioGroupFieldChoice< export interface RadioGroupFieldChoice<
T extends string | number = string | number T extends string | number = string | number
@ -119,6 +83,9 @@ export const RadioGroupField: React.FC<RadioGroupFieldProps> = props => {
[classes.radioLabel]: variant !== "inline", [classes.radioLabel]: variant !== "inline",
[classes.radioLabelInline]: variant === "inline" [classes.radioLabelInline]: variant === "inline"
})} })}
classes={{
label: classes.label
}}
control={ control={
<Radio <Radio
className={classNames({ className={classNames({

View file

@ -0,0 +1,43 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
alignTop: {
alignSelf: "baseline",
position: "relative",
top: -6
},
formLabel: {
marginBottom: theme.spacing(1)
},
radioGroupInline: {
flexDirection: "row"
},
radioLabel: {
alignItems: "start",
marginBottom: theme.spacing(-0.5)
},
radioLabelInline: {
alignItems: "start",
marginRight: theme.spacing(4)
},
label: {
marginTop: theme.spacing(1.5)
},
root: {
"& $radioLabel": {
"&:last-of-type": {
marginBottom: 0
}
},
padding: 0,
width: "100%"
},
rootNoLabel: {
marginTop: theme.spacing(-1.5)
}
}),
{
name: "RadioGroupField"
}
);

View file

@ -0,0 +1,46 @@
import { Typography } from "@material-ui/core";
import React, { ChangeEvent } from "react";
import CardSpacer from "../CardSpacer";
import ControlledCheckbox from "../ControlledCheckbox";
import { useTypeDeleteWarningDialogStyles as useStyles } from "./styles";
interface DeleteWarningDialogConsentContentProps {
description: string | React.ReactNode[];
consentLabel: string;
isConsentChecked: boolean;
onConsentChange: (value: boolean) => void;
}
const DeleteWarningDialogConsentContent: React.FC<DeleteWarningDialogConsentContentProps> = ({
description,
consentLabel,
isConsentChecked,
onConsentChange
}) => {
const classes = useStyles();
const handleConsentChange = ({ target }: ChangeEvent<any>) =>
onConsentChange(target.value);
return (
<>
<Typography>{description}</Typography>
<CardSpacer />
{consentLabel && (
<ControlledCheckbox
name="delete-assigned-items-consent"
checked={isConsentChecked}
onChange={handleConsentChange}
label={
<Typography className={classes.consentLabel}>
{consentLabel}
</Typography>
}
/>
)}
</>
);
};
export default DeleteWarningDialogConsentContent;

View file

@ -1,13 +1,13 @@
import { CardContent, Typography } from "@material-ui/core"; import { CardContent } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer"; import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import ConfirmButton from "@saleor/components/ConfirmButton"; import ConfirmButton from "@saleor/components/ConfirmButton";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import DeleteButton from "@saleor/components/DeleteButton";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import React, { ChangeEvent, useState } from "react"; import React, { useState } from "react";
import { MessageDescriptor, useIntl } from "react-intl"; import { MessageDescriptor, useIntl } from "react-intl";
import DeleteButton from "../DeleteButton";
import DeleteWarningDialogConsentContent from "./DeleteWarningDialogConsentContent";
import { useTypeDeleteWarningDialogStyles as useStyles } from "./styles"; import { useTypeDeleteWarningDialogStyles as useStyles } from "./styles";
interface TypeDeleteWarningDialogContentProps { interface TypeDeleteWarningDialogContentProps {
@ -40,9 +40,6 @@ const TypeDeleteWarningDialogContent: React.FC<TypeDeleteWarningDialogContentPro
const [isConsentChecked, setIsConsentChecked] = useState(false); const [isConsentChecked, setIsConsentChecked] = useState(false);
const handleConsentChange = ({ target }: ChangeEvent<any>) =>
setIsConsentChecked(target.value);
const handleViewAssignedItems = () => navigate(viewAssignedItemsUrl); const handleViewAssignedItems = () => navigate(viewAssignedItemsUrl);
const isDisbled = hasAssignedItems ? !isConsentChecked : false; const isDisbled = hasAssignedItems ? !isConsentChecked : false;
@ -52,26 +49,16 @@ const TypeDeleteWarningDialogContent: React.FC<TypeDeleteWarningDialogContentPro
return ( return (
<CardContent> <CardContent>
<Typography> <DeleteWarningDialogConsentContent
{intl.formatMessage(description, { description={intl.formatMessage(description, {
typeName: singleItemSelectedName, typeName: singleItemSelectedName,
assignedItemsCount, assignedItemsCount,
b: (...chunks) => <b>{chunks}</b> b: (...chunks) => <b>{chunks}</b>
})} })}
</Typography> consentLabel={consentLabel && intl.formatMessage(consentLabel)}
<CardSpacer /> isConsentChecked={isConsentChecked}
{consentLabel && ( onConsentChange={setIsConsentChecked}
<ControlledCheckbox
name="delete-assigned-items-consent"
checked={isConsentChecked}
onChange={handleConsentChange}
label={
<Typography className={classes.consentLabel}>
{intl.formatMessage(consentLabel)}
</Typography>
}
/> />
)}
<CardSpacer /> <CardSpacer />
<div className={classes.buttonsSection}> <div className={classes.buttonsSection}>
{shouldShowViewAssignedItemsButton && ( {shouldShowViewAssignedItemsButton && (

View file

@ -3,6 +3,7 @@ import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import RadioSwitchField from "@saleor/components/RadioSwitchField"; import RadioSwitchField from "@saleor/components/RadioSwitchField";
import useCurrentDate from "@saleor/hooks/useCurrentDate";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import { ChangeEvent } from "@saleor/hooks/useForm"; import { ChangeEvent } from "@saleor/hooks/useForm";
import { makeStyles } from "@saleor/macaw-ui"; import { makeStyles } from "@saleor/macaw-ui";
@ -12,7 +13,6 @@ import classNames from "classnames";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { DateContext } from "../Date/DateContext";
import FormSpacer from "../FormSpacer"; import FormSpacer from "../FormSpacer";
import DateVisibilitySelector from "./DateVisibilitySelector"; import DateVisibilitySelector from "./DateVisibilitySelector";
@ -103,7 +103,7 @@ export const VisibilityCard: React.FC<VisibilityCardProps> = props => {
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const localizeDate = useDateLocalize(); const localizeDate = useDateLocalize();
const dateNow = React.useContext(DateContext); const dateNow = useCurrentDate();
const hasAvailableProps = const hasAvailableProps =
isAvailable !== undefined && availableForPurchase !== undefined; isAvailable !== undefined && availableForPurchase !== undefined;

View file

@ -222,3 +222,10 @@ export const giftCardErrorFragment = gql`
field field
} }
`; `;
export const giftCardSettingsErrorFragment = gql`
fragment GiftCardSettingsErrorFragment on GiftCardSettingsError {
code
field
}
`;

View file

@ -0,0 +1,11 @@
import gql from "graphql-tag";
export const fragmentGiftCardsSettings = gql`
fragment GiftCardsSettingsFragment on GiftCardSettings {
expiryType
expiryPeriod {
type
amount
}
}
`;

View file

@ -182,6 +182,27 @@ export const fragmentOrderDetails = gql`
billingAddress { billingAddress {
...AddressFragment ...AddressFragment
} }
giftCards {
events {
id
type
orderId
balance {
initialBalance {
...Money
}
currentBalance {
...Money
}
oldInitialBalance {
...Money
}
oldCurrentBalance {
...Money
}
}
}
}
isShippingRequired isShippingRequired
canFinalize canFinalize
created created
@ -296,6 +317,7 @@ export const fragmentOrderDetails = gql`
export const fragmentOrderSettings = gql` export const fragmentOrderSettings = gql`
fragment OrderSettingsFragment on OrderSettings { fragment OrderSettingsFragment on OrderSettings {
automaticallyConfirmAllNewOrders automaticallyConfirmAllNewOrders
automaticallyFulfillNonShippableGiftCard
} }
`; `;

View file

@ -7,6 +7,7 @@ export const productTypeFragment = gql`
fragment ProductTypeFragment on ProductType { fragment ProductTypeFragment on ProductType {
id id
name name
kind
hasVariants hasVariants
isShippingRequired isShippingRequired
taxType { taxType {

View file

@ -0,0 +1,16 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardSettingsErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: GiftCardSettingsErrorFragment
// ====================================================
export interface GiftCardSettingsErrorFragment {
__typename: "GiftCardSettingsError";
code: GiftCardSettingsErrorCode;
field: string | null;
}

View file

@ -0,0 +1,22 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardSettingsExpiryTypeEnum, TimePeriodTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: GiftCardsSettingsFragment
// ====================================================
export interface GiftCardsSettingsFragment_expiryPeriod {
__typename: "TimePeriod";
type: TimePeriodTypeEnum;
amount: number;
}
export interface GiftCardsSettingsFragment {
__typename: "GiftCardSettings";
expiryType: GiftCardSettingsExpiryTypeEnum;
expiryPeriod: GiftCardsSettingsFragment_expiryPeriod | null;
}

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { OrderDiscountType, DiscountValueTypeEnum, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, WarehouseClickAndCollectOptionEnum, OrderStatus, OrderAction, JobStatusEnum } from "./../../types/globalTypes"; import { GiftCardEventsEnum, OrderDiscountType, DiscountValueTypeEnum, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, WarehouseClickAndCollectOptionEnum, OrderStatus, OrderAction, JobStatusEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: OrderDetailsFragment // GraphQL fragment: OrderDetailsFragment
@ -43,6 +43,51 @@ export interface OrderDetailsFragment_billingAddress {
streetAddress2: string; streetAddress2: string;
} }
export interface OrderDetailsFragment_giftCards_events_balance_initialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_giftCards_events_balance_currentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_giftCards_events_balance_oldInitialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_giftCards_events_balance_oldCurrentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderDetailsFragment_giftCards_events_balance {
__typename: "GiftCardEventBalance";
initialBalance: OrderDetailsFragment_giftCards_events_balance_initialBalance | null;
currentBalance: OrderDetailsFragment_giftCards_events_balance_currentBalance;
oldInitialBalance: OrderDetailsFragment_giftCards_events_balance_oldInitialBalance | null;
oldCurrentBalance: OrderDetailsFragment_giftCards_events_balance_oldCurrentBalance | null;
}
export interface OrderDetailsFragment_giftCards_events {
__typename: "GiftCardEvent";
id: string;
type: GiftCardEventsEnum | null;
orderId: string | null;
balance: OrderDetailsFragment_giftCards_events_balance | null;
}
export interface OrderDetailsFragment_giftCards {
__typename: "GiftCard";
events: OrderDetailsFragment_giftCards_events[];
}
export interface OrderDetailsFragment_discounts_amount { export interface OrderDetailsFragment_discounts_amount {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -497,6 +542,7 @@ export interface OrderDetailsFragment {
metadata: (OrderDetailsFragment_metadata | null)[]; metadata: (OrderDetailsFragment_metadata | null)[];
privateMetadata: (OrderDetailsFragment_privateMetadata | null)[]; privateMetadata: (OrderDetailsFragment_privateMetadata | null)[];
billingAddress: OrderDetailsFragment_billingAddress | null; billingAddress: OrderDetailsFragment_billingAddress | null;
giftCards: (OrderDetailsFragment_giftCards | null)[] | null;
isShippingRequired: boolean; isShippingRequired: boolean;
canFinalize: boolean; canFinalize: boolean;
created: any; created: any;

View file

@ -10,4 +10,5 @@
export interface OrderSettingsFragment { export interface OrderSettingsFragment {
__typename: "OrderSettings"; __typename: "OrderSettings";
automaticallyConfirmAllNewOrders: boolean; automaticallyConfirmAllNewOrders: boolean;
automaticallyFulfillNonShippableGiftCard: boolean;
} }

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeTypeEnum, MeasurementUnitsEnum, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes"; import { ProductTypeKindEnum, AttributeTypeEnum, MeasurementUnitsEnum, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ProductTypeDetailsFragment // GraphQL fragment: ProductTypeDetailsFragment
@ -63,6 +63,7 @@ export interface ProductTypeDetailsFragment {
__typename: "ProductType"; __typename: "ProductType";
id: string; id: string;
name: string; name: string;
kind: ProductTypeKindEnum;
hasVariants: boolean; hasVariants: boolean;
isShippingRequired: boolean; isShippingRequired: boolean;
taxType: ProductTypeDetailsFragment_taxType | null; taxType: ProductTypeDetailsFragment_taxType | null;

View file

@ -3,6 +3,8 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductTypeKindEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ProductTypeFragment // GraphQL fragment: ProductTypeFragment
// ==================================================== // ====================================================
@ -17,6 +19,7 @@ export interface ProductTypeFragment {
__typename: "ProductType"; __typename: "ProductType";
id: string; id: string;
name: string; name: string;
kind: ProductTypeKindEnum;
hasVariants: boolean; hasVariants: boolean;
isShippingRequired: boolean; isShippingRequired: boolean;
taxType: ProductTypeFragment_taxType | null; taxType: ProductTypeFragment_taxType | null;

View file

@ -1,11 +1,14 @@
import { Dialog, DialogTitle } from "@material-ui/core"; import { Dialog, DialogTitle } from "@material-ui/core";
import { IMessage } from "@saleor/components/messages"; import { IMessage } from "@saleor/components/messages";
import useCurrentDate from "@saleor/hooks/useCurrentDate";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { GiftCardCreateInput } from "@saleor/types/globalTypes"; import { GiftCardCreateInput } from "@saleor/types/globalTypes";
import commonErrorMessages from "@saleor/utils/errors/common"; import commonErrorMessages from "@saleor/utils/errors/common";
import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers";
import React, { useState } from "react"; import React, { useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { GIFT_CARD_LIST_QUERY } from "../GiftCardsList/types";
import ContentWithProgress from "./ContentWithProgress"; import ContentWithProgress from "./ContentWithProgress";
import GiftCardCreateDialogCodeContent from "./GiftCardCreateDialogCodeContent"; import GiftCardCreateDialogCodeContent from "./GiftCardCreateDialogCodeContent";
import GiftCardCreateDialogForm, { import GiftCardCreateDialogForm, {
@ -15,15 +18,10 @@ import { giftCardCreateDialogMessages as messages } from "./messages";
import { useGiftCardCreateMutation } from "./mutations"; import { useGiftCardCreateMutation } from "./mutations";
import { useChannelCurrencies } from "./queries"; import { useChannelCurrencies } from "./queries";
import { GiftCardCreate } from "./types/GiftCardCreate"; import { GiftCardCreate } from "./types/GiftCardCreate";
import { getGiftCardExpirySettingsInputData } from "./utils"; import { getGiftCardExpiryInputData } from "./utils";
interface GiftCardCreateDialogProps { const GiftCardCreateDialog: React.FC<DialogActionHandlersProps> = ({
onClose: () => void; closeDialog,
open: boolean;
}
const GiftCardCreateDialog: React.FC<GiftCardCreateDialogProps> = ({
onClose,
open open
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
@ -56,6 +54,8 @@ const GiftCardCreateDialog: React.FC<GiftCardCreateDialogProps> = ({
} }
}; };
const currentDate = useCurrentDate();
const getParsedSubmitInputData = ( const getParsedSubmitInputData = (
formData: GiftCardCreateFormData formData: GiftCardCreateFormData
): GiftCardCreateInput => { ): GiftCardCreateInput => {
@ -64,23 +64,29 @@ const GiftCardCreateDialog: React.FC<GiftCardCreateDialogProps> = ({
balanceCurrency, balanceCurrency,
note, note,
tag, tag,
selectedCustomer sendToCustomerSelected,
selectedCustomer,
requiresActivation,
channelSlug
} = formData; } = formData;
return { return {
note: note || null, note: note || null,
tag: tag || null, tag: tag || null,
userEmail: selectedCustomer.email || null, userEmail: (sendToCustomerSelected && selectedCustomer.email) || null,
channel: (sendToCustomerSelected && channelSlug) || null,
balance: { balance: {
amount: balanceAmount, amount: balanceAmount,
currency: balanceCurrency currency: balanceCurrency
}, },
expirySettings: getGiftCardExpirySettingsInputData(formData) expiryDate: getGiftCardExpiryInputData(formData, currentDate),
isActive: !requiresActivation
}; };
}; };
const [createGiftCard, createGiftCardOpts] = useGiftCardCreateMutation({ const [createGiftCard, createGiftCardOpts] = useGiftCardCreateMutation({
onCompleted onCompleted,
refetchQueries: [GIFT_CARD_LIST_QUERY]
}); });
const handleSubmit = (data: GiftCardCreateFormData) => { const handleSubmit = (data: GiftCardCreateFormData) => {
@ -92,7 +98,7 @@ const GiftCardCreateDialog: React.FC<GiftCardCreateDialogProps> = ({
}; };
const handleClose = () => { const handleClose = () => {
onClose(); closeDialog();
// dialog closing animation runs slower than prop change // dialog closing animation runs slower than prop change
// and we don't want to show the form for a split second // and we don't want to show the form for a split second
setTimeout(() => setCardCode(null), 0); setTimeout(() => setCardCode(null), 0);

View file

@ -1,33 +1,51 @@
import { DialogContent, Divider, TextField } from "@material-ui/core"; import {
DialogContent,
Divider,
TextField,
Typography
} from "@material-ui/core";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer"; import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import DialogButtons from "@saleor/components/ActionDialog/DialogButtons"; import DialogButtons from "@saleor/components/ActionDialog/DialogButtons";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import TextWithSelectField from "@saleor/components/TextWithSelectField"; import TextWithSelectField from "@saleor/components/TextWithSelectField";
import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import GiftCardExpirySelect from "@saleor/giftCards/components/GiftCardExpirySelect";
import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput"; import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput";
import useForm from "@saleor/hooks/useForm"; import useForm from "@saleor/hooks/useForm";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
import Label from "@saleor/orders/components/OrderHistory/Label"; import Label from "@saleor/orders/components/OrderHistory/Label";
import { import {
GiftCardExpiryTypeEnum, GiftCardSettingsExpiryTypeEnum,
TimePeriodTypeEnum TimePeriodTypeEnum
} from "@saleor/types/globalTypes"; } from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import { mapSingleValueNodeToChoice } from "@saleor/utils/maps"; import { mapSingleValueNodeToChoice } from "@saleor/utils/maps";
import React, { useState } from "react"; import React, { useState } from "react";
import { useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import GiftCardSendToCustomer from "../components/GiftCardSendToCustomer/GiftCardSendToCustomer";
import { useGiftCardSettingsQuery } from "../GiftCardSettings/queries";
import { getGiftCardErrorMessage } from "../GiftCardUpdate/messages"; import { getGiftCardErrorMessage } from "../GiftCardUpdate/messages";
import GiftCardCustomerSelectField from "./GiftCardCustomerSelectField"; import GiftCardCreateExpirySelect from "./GiftCardCreateExpirySelect";
import { giftCardCreateDialogMessages as messages } from "./messages"; import { giftCardCreateDialogMessages as messages } from "./messages";
import { useGiftCardCreateDialogFormStyles as useStyles } from "./styles"; import { useGiftCardCreateDialogFormStyles as useStyles } from "./styles";
import { GiftCardCommonFormData, GiftCardCreateFormCustomer } from "./types"; import {
GiftCardCommonFormData,
GiftCardCreateFormCustomer,
GiftCardExpiryType
} from "./types";
export interface GiftCardCreateFormData extends GiftCardCommonFormData { export interface GiftCardCreateFormData extends GiftCardCommonFormData {
note: string; note: string;
sendToCustomerSelected: boolean;
selectedCustomer?: GiftCardCreateFormCustomer; selectedCustomer?: GiftCardCreateFormCustomer;
channelSlug?: string;
expirySelected: boolean;
expiryType: GiftCardExpiryType;
expiryPeriodType: TimePeriodTypeEnum;
expiryPeriodAmount: number;
requiresActivation: boolean;
} }
const initialCustomer = { email: "", name: "" }; const initialCustomer = { email: "", name: "" };
@ -37,10 +55,13 @@ export const initialData: GiftCardCreateFormData = {
balanceAmount: 1, balanceAmount: 1,
balanceCurrency: null, balanceCurrency: null,
note: "", note: "",
sendToCustomerSelected: false,
expirySelected: false,
expiryType: "EXPIRY_PERIOD",
expiryDate: "", expiryDate: "",
expiryType: GiftCardExpiryTypeEnum.EXPIRY_PERIOD, expiryPeriodType: TimePeriodTypeEnum.MONTH,
expiryPeriodType: TimePeriodTypeEnum.YEAR, expiryPeriodAmount: 12,
expiryPeriodAmount: 1 requiresActivation: true
}; };
interface GiftCardCreateDialogFormProps { interface GiftCardCreateDialogFormProps {
@ -63,6 +84,11 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
const initialCurrency = channelCurrencies[0]; const initialCurrency = channelCurrencies[0];
const {
data: settingsData,
loading: loadingSettings
} = useGiftCardSettingsQuery();
const [selectedCustomer, setSelectedCustomer] = useState< const [selectedCustomer, setSelectedCustomer] = useState<
GiftCardCreateFormCustomer GiftCardCreateFormCustomer
>(initialCustomer); >(initialCustomer);
@ -70,33 +96,65 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
const handleSubmit = (data: GiftCardCreateFormData) => const handleSubmit = (data: GiftCardCreateFormData) =>
onSubmit({ ...data, selectedCustomer }); onSubmit({ ...data, selectedCustomer });
const getInitialExpirySettingsData = (): Partial<GiftCardCreateFormData> => {
if (loadingSettings) {
return {};
}
const { expiryType, expiryPeriod } = settingsData?.giftCardSettings;
if (expiryType === GiftCardSettingsExpiryTypeEnum.NEVER_EXPIRE) {
return {};
}
return {
expiryType,
expiryPeriodType: expiryPeriod?.type,
expiryPeriodAmount: expiryPeriod?.amount
};
};
const { submit, change, data } = useForm( const { submit, change, data } = useForm(
{ ...initialData, balanceCurrency: initialCurrency }, {
...initialData,
...getInitialExpirySettingsData(),
balanceCurrency: initialCurrency,
channelSlug: ""
},
handleSubmit handleSubmit
); );
const formErrors = getFormErrors( const formErrors = getFormErrors(
[ ["tag", "expiryDate", "customer", "currency", "amount", "balance"],
"tag",
"expiryDate",
"expiryPeriod",
"customer",
"currency",
"amount",
"balance"
],
apiErrors apiErrors
); );
const { const {
tag, tag,
sendToCustomerSelected,
channelSlug,
balanceAmount,
balanceCurrency,
expirySelected,
expiryType,
expiryPeriodAmount, expiryPeriodAmount,
expiryPeriodType, expiryPeriodType,
expiryType, expiryDate,
balanceAmount, requiresActivation
balanceCurrency
} = data; } = data;
const shouldEnableSubmitButton = () => {
if (!balanceAmount) {
return false;
}
if (expirySelected && expiryType === "EXPIRY_DATE") {
return !!expiryDate;
}
return true;
};
return ( return (
<> <>
<DialogContent> <DialogContent>
@ -107,7 +165,7 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
choices={mapSingleValueNodeToChoice(channelCurrencies)} choices={mapSingleValueNodeToChoice(channelCurrencies)}
containerClassName={classes.balanceContainer} containerClassName={classes.balanceContainer}
textFieldProps={{ textFieldProps={{
type: "number", type: "float",
label: intl.formatMessage(messages.amountLabel), label: intl.formatMessage(messages.amountLabel),
name: "balanceAmount", name: "balanceAmount",
value: balanceAmount, value: balanceAmount,
@ -128,24 +186,25 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
/> />
<CardSpacer /> <CardSpacer />
<Divider /> <Divider />
<CardSpacer /> <GiftCardSendToCustomer
<GiftCardCustomerSelectField selectedChannelSlug={channelSlug}
change={change}
sendToCustomerSelected={sendToCustomerSelected}
selectedCustomer={selectedCustomer} selectedCustomer={selectedCustomer}
setSelectedCustomer={setSelectedCustomer} setSelectedCustomer={setSelectedCustomer}
/> />
<VerticalSpacer />
<Label text={intl.formatMessage(messages.customerSubtitle)} />
<CardSpacer />
<Divider /> <Divider />
<CardSpacer /> <VerticalSpacer />
<GiftCardExpirySelect <GiftCardCreateExpirySelect
errors={formErrors} errors={formErrors}
change={change} change={change}
expirySelected={expirySelected}
expiryType={expiryType} expiryType={expiryType}
expiryPeriodAmount={expiryPeriodAmount} expiryPeriodAmount={expiryPeriodAmount}
expiryPeriodType={expiryPeriodType} expiryPeriodType={expiryPeriodType}
expiryDate={expiryDate}
/> />
<CardSpacer /> <VerticalSpacer />
<TextField <TextField
name="note" name="note"
onChange={change} onChange={change}
@ -157,8 +216,23 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
/> />
<VerticalSpacer /> <VerticalSpacer />
<Label text={intl.formatMessage(messages.noteSubtitle)} /> <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}
onChange={change}
/>
</DialogContent> </DialogContent>
<DialogButtons <DialogButtons
disabled={!shouldEnableSubmitButton()}
onConfirm={submit} onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.issueButtonLabel)} confirmButtonLabel={intl.formatMessage(messages.issueButtonLabel)}
confirmButtonState={opts?.status} confirmButtonState={opts?.status}

View file

@ -0,0 +1,137 @@
import { TextField, Typography } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import RadioGroupField from "@saleor/components/RadioGroupField";
import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import TimePeriodField from "@saleor/giftCards/components/TimePeriodField";
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 { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
import React from "react";
import { FormattedMessage } from "react-intl";
import { MessageDescriptor, useIntl } from "react-intl";
import { giftCardCreateExpirySelectMessages as messages } from "./messages";
import { useGiftCardCreateExpirySelectStyles as useStyles } from "./styles";
interface UntranslatedOption {
label: MessageDescriptor;
value: GiftCardExpiryType;
}
const options: UntranslatedOption[] = [
{
label: messages.expiryPeriodLabel,
value: "EXPIRY_PERIOD"
},
{
label: messages.expiryDateLabel,
value: "EXPIRY_DATE"
}
];
interface GiftCardCreateExpirySelectProps {
change: FormChange;
expirySelected: boolean;
expiryPeriodType: TimePeriodTypeEnum;
expiryPeriodAmount: number;
expiryType: GiftCardExpiryType;
customOptions?: UntranslatedOption[];
errors?: Record<"expiryDate", GiftCardError>;
expiryDate?: string;
}
const GiftCardCreateExpirySelect: React.FC<GiftCardCreateExpirySelectProps> = ({
errors,
change,
expirySelected,
expiryPeriodType,
expiryPeriodAmount,
expiryType,
expiryDate
}) => {
const intl = useIntl();
const classes = useStyles({});
const translatedOptions = options.map(({ label, value }) => ({
value,
label: intl.formatMessage(label)
}));
const currentDate = useCurrentDate();
return (
<>
<ControlledCheckbox
name={"expirySelected"}
label={intl.formatMessage(messages.expirySelectedLabel)}
checked={expirySelected}
onChange={change}
/>
{expirySelected && (
<>
<VerticalSpacer spacing={2} />
<RadioGroupField
innerContainerClassName={classes.radioGroupContainer}
choices={translatedOptions}
onChange={change}
name={"expiryType"}
value={expiryType}
variant="inline"
/>
<VerticalSpacer spacing={2} />
{expiryType === "EXPIRY_DATE" && (
<TextField
error={!!errors?.expiryDate}
helperText={getGiftCardErrorMessage(errors?.expiryDate, intl)}
onChange={change}
name={"expiryDate"}
className={classes.dateField}
label={intl.formatMessage(messages.expiryDateLabel)}
value={expiryDate}
InputLabelProps={{
shrink: true
}}
type="date"
/>
)}
{expiryType === "EXPIRY_PERIOD" && (
<div className={classes.periodField}>
<TimePeriodField
isError={!!errors?.expiryDate}
helperText={getGiftCardErrorMessage(errors?.expiryDate, intl)}
change={change}
periodType={expiryPeriodType}
periodAmount={expiryPeriodAmount}
amountFieldName={"expiryPeriodAmount"}
typeFieldName={"expiryPeriodType"}
/>
<HorizontalSpacer spacing={2} />
<div className={classes.dateText}>
<Typography variant="caption">
<FormattedMessage {...messages.expiryOnLabel} />
</Typography>
<Typography>
{getExpiryPeriodTerminationDate(
currentDate,
expiryPeriodType,
expiryPeriodAmount
)?.format("L")}
</Typography>
</div>
</div>
)}
<VerticalSpacer spacing={2} />
</>
)}
</>
);
};
export default GiftCardCreateExpirySelect;

View file

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

View file

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

View file

@ -0,0 +1,20 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useGiftCardCreateExpirySelectStyles = makeStyles(
theme => ({
radioGroupContainer: {
display: "flex",
flexDirection: "row"
},
dateField: {
width: 400
},
periodField: {
display: "flex"
},
dateText: {
marginTop: theme.spacing(0.5)
}
}),
{ name: "GiftCardExpirySelect" }
);

View file

@ -17,11 +17,6 @@ export const giftCardCreateDialogMessages = defineMessages({
defaultMessage: "Customer", defaultMessage: "Customer",
description: "GiftCardCreateDialog customer label" description: "GiftCardCreateDialog customer label"
}, },
customerSubtitle: {
defaultMessage:
"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.",
description: "GiftCardCreateDialog customer subtitle"
},
noteLabel: { noteLabel: {
defaultMessage: "Note", defaultMessage: "Note",
description: "GiftCardCreateDialog note label" description: "GiftCardCreateDialog note label"
@ -46,5 +41,13 @@ export const giftCardCreateDialogMessages = defineMessages({
createdSuccessAlertTitle: { createdSuccessAlertTitle: {
defaultMessage: "Successfully created gift card", defaultMessage: "Successfully created gift card",
description: "GiftCardCreateDialog createdSuccessAlertTitle" description: "GiftCardCreateDialog createdSuccessAlertTitle"
},
requiresActivationLabel: {
defaultMessage: "Requires activation",
description: "GiftCarUpdateDetailsExpirySection requires activation label"
},
requiresActivationCaption: {
defaultMessage: "All issued cards require activation by staff before use.",
description: "GiftCarUpdateDetailsExpirySection requires activation caption"
} }
}); });

View file

@ -1,9 +1,9 @@
import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { FormChange } from "@saleor/hooks/useForm"; import { FormChange } from "@saleor/hooks/useForm";
import {
GiftCardExpiryTypeEnum, import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm";
TimePeriodTypeEnum
} from "@saleor/types/globalTypes"; export type GiftCardExpiryType = "EXPIRY_DATE" | "EXPIRY_PERIOD";
export interface GiftCardCreateFormCustomer { export interface GiftCardCreateFormCustomer {
name: string; name: string;
@ -15,16 +15,22 @@ export interface GiftCardCommonFormData {
balanceAmount: number; balanceAmount: number;
balanceCurrency: string; balanceCurrency: string;
expiryDate: string; expiryDate: string;
expiryType: GiftCardExpiryTypeEnum;
expiryPeriodType: TimePeriodTypeEnum;
expiryPeriodAmount: number;
} }
export type GiftCardCreateFormErrors = Record< export type GiftCardCreateFormErrors = Record<
"tag" | "expiryDate" | "expiryPeriod" | "customer" | "currency" | "amount", "tag" | "expiryDate" | "customer" | "currency" | "amount",
GiftCardError GiftCardError
>; >;
export type GiftCardCreateInputData = Pick<
GiftCardCreateFormData,
| "expirySelected"
| "expiryDate"
| "expiryPeriodAmount"
| "expiryPeriodType"
| "expiryType"
>;
export interface GiftCardCreateFormCommonProps { export interface GiftCardCreateFormCommonProps {
change: FormChange; change: FormChange;
errors: GiftCardCreateFormErrors; errors: GiftCardCreateFormErrors;

View file

@ -1,41 +1,54 @@
import { import { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
GiftCardExpirySettingsInput, import moment from "moment-timezone";
GiftCardExpiryTypeEnum
} from "@saleor/types/globalTypes";
import { GiftCardCommonFormData } from "./types"; import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm";
export const getGiftCardExpirySettingsInputData = ({ const addToCurrentDate = (
currentDate: number,
expiryPeriodAmount: number,
unit: moment.unitOfTime.DurationConstructor
) => moment(currentDate).add(expiryPeriodAmount, unit);
export const getExpiryPeriodTerminationDate = (
currentDate: number,
expiryPeriodType: TimePeriodTypeEnum,
expiryPeriodAmount: number = 0
): moment.Moment | null => {
switch (expiryPeriodType) {
case TimePeriodTypeEnum.DAY:
return addToCurrentDate(currentDate, expiryPeriodAmount, "d");
case TimePeriodTypeEnum.WEEK:
return addToCurrentDate(currentDate, expiryPeriodAmount, "w");
case TimePeriodTypeEnum.MONTH:
return addToCurrentDate(currentDate, expiryPeriodAmount, "M");
case TimePeriodTypeEnum.YEAR:
return addToCurrentDate(currentDate, expiryPeriodAmount, "y");
default:
return null;
}
};
export const getGiftCardExpiryInputData = (
{
expirySelected,
expiryType, expiryType,
expiryDate, expiryDate,
expiryPeriodAmount, expiryPeriodAmount,
expiryPeriodType expiryPeriodType
}: Pick< }: GiftCardCreateFormData,
GiftCardCommonFormData, currentDate: number
"expiryDate" | "expiryPeriodAmount" | "expiryPeriodType" | "expiryType" ): string => {
>): GiftCardExpirySettingsInput => { if (!expirySelected) {
switch (expiryType) { return;
case GiftCardExpiryTypeEnum.EXPIRY_DATE: {
return {
expiryType,
expiryDate
};
} }
case GiftCardExpiryTypeEnum.EXPIRY_PERIOD: { if (expiryType === "EXPIRY_PERIOD") {
return { return getExpiryPeriodTerminationDate(
expiryType, currentDate,
expiryPeriod: { expiryPeriodType,
amount: expiryPeriodAmount, expiryPeriodAmount
type: expiryPeriodType )?.format("YYYY-MM-DD");
}
};
} }
default: { return expiryDate;
return {
expiryType
};
}
}
}; };

View file

@ -0,0 +1,45 @@
import { Card, CardContent } from "@material-ui/core";
import CardTitle from "@saleor/components/CardTitle";
import GiftCardSettingsExpirySelect, {
GiftCardSettingsExpirySelectProps
} from "@saleor/giftCards/components/GiftCardSettingsExpirySelect";
import React from "react";
import { useIntl } from "react-intl";
import { GiftCardSettingsFormData } from "../types";
import { giftCardExpirySettingsCard as messages } from "./messages";
export interface GiftCardExpirySettingsCardProps
extends Pick<GiftCardSettingsExpirySelectProps, "errors"> {
data: GiftCardSettingsFormData;
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
}
const GiftCardExpirySettingsCard: React.FC<GiftCardExpirySettingsCardProps> = ({
data,
disabled,
errors,
onChange
}) => {
const intl = useIntl();
return (
<Card data-test="giftCardSettings">
<CardTitle title={intl.formatMessage(messages.expiryDateTitle)} />
<CardContent>
<GiftCardSettingsExpirySelect
expiryPeriodActive={data.expiryPeriodActive}
expiryPeriodType={data.expiryPeriodType}
expiryPeriodAmount={data.expiryPeriodAmount}
change={onChange}
disabled={disabled}
errors={errors}
/>
</CardContent>
</Card>
);
};
GiftCardExpirySettingsCard.displayName = "GiftCardExpirySettingsCard";
export default GiftCardExpirySettingsCard;

View file

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

View file

@ -0,0 +1,12 @@
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."
},
expiryDateTitle: {
defaultMessage: "Expiry date",
description: "section header"
}
});

View file

@ -0,0 +1,101 @@
import { Typography } from "@material-ui/core";
import Container from "@saleor/components/Container";
import Form from "@saleor/components/Form";
import { Grid } from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
import Savebar from "@saleor/components/Savebar";
import useNavigator from "@saleor/hooks/useNavigator";
import { sectionNames } from "@saleor/intl";
import { Backlink } from "@saleor/macaw-ui";
import {
GiftCardSettingsExpiryTypeEnum,
TimePeriodTypeEnum
} from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { giftCardsListPath } from "../urls";
import GiftCardExpirySettingsCard from "./GiftCardExpirySettingsCard";
import { giftCardExpirySettingsCard as expirySettingsMessages } from "./GiftCardExpirySettingsCard/messages";
import { giftCardSettingsPageMessages as messages } from "./messages";
import { useGiftCardSettingsUpdateMutation } from "./mutations";
import { useGiftCardSettingsQuery } from "./queries";
import { GiftCardSettingsFormData } from "./types";
import { getGiftCardSettingsInputData } from "./utils";
const GiftCardSettingsPage: React.FC = () => {
const intl = useIntl();
const navigate = useNavigator();
const { data, loading } = useGiftCardSettingsQuery();
const settingsData = data?.giftCardSettings;
const initialData: GiftCardSettingsFormData = {
expiryPeriodActive:
settingsData?.expiryType === GiftCardSettingsExpiryTypeEnum.EXPIRY_PERIOD,
expiryPeriodType:
settingsData?.expiryPeriod?.type || TimePeriodTypeEnum.YEAR,
expiryPeriodAmount: settingsData?.expiryPeriod?.amount || 1
};
const [
updateGiftCardSettings,
updateGiftCardSettingsOpts
] = useGiftCardSettingsUpdateMutation({});
const navigateBack = () => navigate(giftCardsListPath);
const handleSubmit = (formData: GiftCardSettingsFormData) => {
updateGiftCardSettings({
variables: {
input: getGiftCardSettingsInputData(formData)
}
});
};
const formLoading = loading || updateGiftCardSettingsOpts?.loading;
const apiErrors =
updateGiftCardSettingsOpts?.data?.giftCardSettingsUpdate?.errors;
const formErrors = getFormErrors(["expiryPeriod"], apiErrors);
return (
<Container>
<Backlink onClick={navigateBack}>
{intl.formatMessage(sectionNames.giftCards)}
</Backlink>
<PageHeader title={intl.formatMessage(messages.title)} underline={true} />
<Form initial={initialData} onSubmit={handleSubmit}>
{({ data: formData, submit, hasChanged, change }) => (
<Grid variant="inverted">
<div>
<Typography>
<FormattedMessage
{...expirySettingsMessages.expiryDateSectionDescription}
/>
</Typography>
</div>
<GiftCardExpirySettingsCard
data={formData}
disabled={formLoading}
onChange={change}
errors={formErrors}
/>
<Savebar
onCancel={navigateBack}
onSubmit={submit}
disabled={formLoading || !hasChanged}
state={updateGiftCardSettingsOpts?.status}
/>
</Grid>
)}
</Form>
</Container>
);
};
export default GiftCardSettingsPage;

View file

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

View file

@ -0,0 +1,17 @@
import { GiftCardSettingsErrorFragment } from "@saleor/fragments/types/GiftCardSettingsErrorFragment";
import { getCommonFormFieldErrorMessage } from "@saleor/utils/errors/common";
import { defineMessages, IntlShape } from "react-intl";
export const giftCardSettingsPageMessages = defineMessages({
title: {
defaultMessage: "Gift Cards Settings",
description: "header"
}
});
export function getGiftCardSettingsErrorMessage(
error: Omit<GiftCardSettingsErrorFragment, "__typename"> | undefined,
intl: IntlShape
): string {
return getCommonFormFieldErrorMessage(error, intl);
}

View file

@ -0,0 +1,29 @@
import { giftCardSettingsErrorFragment } from "@saleor/fragments/errors";
import { fragmentGiftCardsSettings } from "@saleor/fragments/giftCards";
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import {
GiftCardSettingsUpdate,
GiftCardSettingsUpdateVariables
} from "./types/GiftCardSettingsUpdate";
const giftCardSettingsUpdate = gql`
${giftCardSettingsErrorFragment}
${fragmentGiftCardsSettings}
mutation GiftCardSettingsUpdate($input: GiftCardSettingsUpdateInput!) {
giftCardSettingsUpdate(input: $input) {
errors {
...GiftCardSettingsErrorFragment
}
giftCardSettings {
...GiftCardsSettingsFragment
}
}
}
`;
export const useGiftCardSettingsUpdateMutation = makeMutation<
GiftCardSettingsUpdate,
GiftCardSettingsUpdateVariables
>(giftCardSettingsUpdate);

View file

@ -0,0 +1,18 @@
import { fragmentGiftCardsSettings } from "@saleor/fragments/giftCards";
import makeQuery from "@saleor/hooks/makeQuery";
import gql from "graphql-tag";
import { GiftCardSettings } from "./types/GiftCardSettings";
export const giftCardSettings = gql`
${fragmentGiftCardsSettings}
query GiftCardSettings {
giftCardSettings {
...GiftCardsSettingsFragment
}
}
`;
export const useGiftCardSettingsQuery = makeQuery<GiftCardSettings, never>(
giftCardSettings
);

View file

@ -0,0 +1,7 @@
import { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
export interface GiftCardSettingsFormData {
expiryPeriodActive: boolean;
expiryPeriodType: TimePeriodTypeEnum;
expiryPeriodAmount: number;
}

View file

@ -0,0 +1,26 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardSettingsExpiryTypeEnum, TimePeriodTypeEnum } from "./../../../types/globalTypes";
// ====================================================
// GraphQL query operation: GiftCardSettings
// ====================================================
export interface GiftCardSettings_giftCardSettings_expiryPeriod {
__typename: "TimePeriod";
type: TimePeriodTypeEnum;
amount: number;
}
export interface GiftCardSettings_giftCardSettings {
__typename: "GiftCardSettings";
expiryType: GiftCardSettingsExpiryTypeEnum;
expiryPeriod: GiftCardSettings_giftCardSettings_expiryPeriod | null;
}
export interface GiftCardSettings {
giftCardSettings: GiftCardSettings_giftCardSettings;
}

View file

@ -0,0 +1,42 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardSettingsUpdateInput, GiftCardSettingsErrorCode, GiftCardSettingsExpiryTypeEnum, TimePeriodTypeEnum } from "./../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: GiftCardSettingsUpdate
// ====================================================
export interface GiftCardSettingsUpdate_giftCardSettingsUpdate_errors {
__typename: "GiftCardSettingsError";
code: GiftCardSettingsErrorCode;
field: string | null;
}
export interface GiftCardSettingsUpdate_giftCardSettingsUpdate_giftCardSettings_expiryPeriod {
__typename: "TimePeriod";
type: TimePeriodTypeEnum;
amount: number;
}
export interface GiftCardSettingsUpdate_giftCardSettingsUpdate_giftCardSettings {
__typename: "GiftCardSettings";
expiryType: GiftCardSettingsExpiryTypeEnum;
expiryPeriod: GiftCardSettingsUpdate_giftCardSettingsUpdate_giftCardSettings_expiryPeriod | null;
}
export interface GiftCardSettingsUpdate_giftCardSettingsUpdate {
__typename: "GiftCardSettingsUpdate";
errors: GiftCardSettingsUpdate_giftCardSettingsUpdate_errors[];
giftCardSettings: GiftCardSettingsUpdate_giftCardSettingsUpdate_giftCardSettings | null;
}
export interface GiftCardSettingsUpdate {
giftCardSettingsUpdate: GiftCardSettingsUpdate_giftCardSettingsUpdate | null;
}
export interface GiftCardSettingsUpdateVariables {
input: GiftCardSettingsUpdateInput;
}

View file

@ -0,0 +1,32 @@
import {
GiftCardSettingsExpiryTypeEnum,
GiftCardSettingsUpdateInput
} from "@saleor/types/globalTypes";
import { GiftCardSettingsFormData } from "./types";
export const getGiftCardSettingsInputData = ({
expiryPeriodActive,
expiryPeriodType,
expiryPeriodAmount
}: Pick<
GiftCardSettingsFormData,
"expiryPeriodActive" | "expiryPeriodType" | "expiryPeriodAmount"
>): GiftCardSettingsUpdateInput => {
const expiryType = expiryPeriodActive
? GiftCardSettingsExpiryTypeEnum.EXPIRY_PERIOD
: GiftCardSettingsExpiryTypeEnum.NEVER_EXPIRE;
const expiryPeriod =
expiryPeriodActive && expiryPeriodType && expiryPeriodAmount
? {
type: expiryPeriodType,
amount: expiryPeriodAmount
}
: undefined;
return {
expiryType,
expiryPeriod
};
};

View file

@ -0,0 +1,179 @@
import { CircularProgress, TextField, Typography } from "@material-ui/core";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import { useChannelsList } from "@saleor/channels/queries";
import ActionDialog from "@saleor/components/ActionDialog";
import { useChannelsSearch } from "@saleor/components/ChannelsAvailabilityDialog/utils";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { IMessage } from "@saleor/components/messages";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import useForm from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier";
import { getBySlug } from "@saleor/products/components/ProductVariantCreatorPage/utils";
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";
import { useGiftCardDeleteDialogContentStyles as useProgressStyles } from "../../components/GiftCardDeleteDialog/styles";
import { useUpdateBalanceDialogStyles as useStyles } from "../GiftCardUpdateBalanceDialog/styles";
import { getGiftCardErrorMessage } from "../messages";
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
import { giftCardResendCodeDialogMessages as messages } from "./messages";
import { useGiftCardResendCodeMutation } from "./mutations";
import { GiftCardResend } from "./types/GiftCardResend";
import { useDialogFormReset } from "./utils";
export interface GiftCardResendCodeFormData {
email: string;
channelSlug: string;
}
const GiftCardResendCodeDialog: React.FC<DialogActionHandlersProps> = ({
open,
closeDialog
}) => {
const intl = useIntl();
const notify = useNotifier();
const classes = useStyles();
const progressClasses = useProgressStyles();
const {
giftCard: { boughtInChannel: initialChannelSlug }
} = useGiftCardDetails();
const [consentSelected, setConsentSelected] = useState(false);
const { data: channelsData, loading: loadingChannels } = useChannelsList({});
const channels = channelsData?.channels;
const activeChannels = channels?.filter(({ isActive }) => isActive);
const { onQueryChange, filteredChannels } = useChannelsSearch(activeChannels);
const initialFormData: GiftCardResendCodeFormData = {
email: "",
channelSlug: initialChannelSlug || ""
};
const {
giftCard: { id }
} = useGiftCardDetails();
const handleSubmit = async ({
email,
channelSlug
}: GiftCardResendCodeFormData) => {
const result = await resendGiftCardCode({
variables: {
input: {
id,
email: email ? email : null,
channel: channelSlug
}
}
});
return result?.data?.giftCardResend?.errors;
};
const { data, change, submit, reset } = useForm(
initialFormData,
handleSubmit
);
const onCompleted = (data: GiftCardResend) => {
const errors = data?.giftCardResend?.errors;
const notifierData: IMessage = !!errors?.length
? {
status: "error",
text: intl.formatMessage(commonErrorMessages.unknownError)
}
: {
status: "success",
text: intl.formatMessage(messages.successResendAlertText)
};
notify(notifierData);
if (!errors.length) {
closeDialog();
reset();
}
};
const [
resendGiftCardCode,
resendGiftCardCodeOpts
] = useGiftCardResendCodeMutation({
onCompleted
});
const { loading, status, data: submitData } = resendGiftCardCodeOpts;
const { formErrors } = useDialogFormReset({
open,
reset,
apiErrors: submitData?.giftCardResend?.errors,
keys: ["email"]
});
useEffect(reset, [consentSelected]);
return (
<ActionDialog
maxWidth="sm"
open={open}
onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.submitButtonLabel)}
onClose={closeDialog}
title={intl.formatMessage(messages.title)}
confirmButtonState={status}
disabled={loading}
>
{loadingChannels ? (
<div className={progressClasses.progressContainer}>
<CircularProgress />
</div>
) : (
<>
<Typography>{intl.formatMessage(messages.description)}</Typography>
<VerticalSpacer />
<SingleAutocompleteSelectField
choices={mapSlugNodeToChoice(filteredChannels)}
name="channelSlug"
label={intl.formatMessage(messages.sendToChannelSelectLabel)}
value={data?.channelSlug}
onChange={change}
displayValue={channels.find(getBySlug(data?.channelSlug))?.name}
fetchChoices={onQueryChange}
/>
<VerticalSpacer />
<ControlledCheckbox
name="differentMailConsent"
label={intl.formatMessage(messages.consentCheckboxLabel)}
checked={consentSelected}
onChange={(event: React.ChangeEvent<any>) =>
setConsentSelected(event.target.value)
}
/>
<VerticalSpacer />
<TextField
disabled={!consentSelected}
error={!!formErrors?.email}
helperText={getGiftCardErrorMessage(formErrors?.email, intl)}
name="email"
value={data.email}
onChange={change}
className={classes.inputContainer}
label={intl.formatMessage(messages.emailInputPlaceholder)}
/>
</>
)}
</ActionDialog>
);
};
export default GiftCardResendCodeDialog;

View file

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

View file

@ -0,0 +1,33 @@
import { defineMessages } from "react-intl";
export const giftCardResendCodeDialogMessages = defineMessages({
title: {
defaultMessage: "Resend code to customer",
description: "GiftCardResendCodeDialog 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"
},
consentCheckboxLabel: {
defaultMessage: "Yes, I want to send gift card to different address",
description: "GiftCardResendCodeDialog consentCheckboxLabel"
},
submitButtonLabel: {
defaultMessage: "Resend",
description: "GiftCardResendCodeDialog submitButtonLabel"
},
emailInputPlaceholder: {
defaultMessage: "Provided email address",
description: "GiftCardResendCodeDialog emailInputPlaceholder"
},
successResendAlertText: {
defaultMessage: "Successfully resent code to customer!",
description: "GiftCardResendCodeDialog successResendAlertText"
},
sendToChannelSelectLabel: {
defaultMessage: "Send to channel",
description: "ChannelPickerSelectField sendToChannelLabel"
}
});

View file

@ -0,0 +1,29 @@
import { giftCardErrorFragment } from "@saleor/fragments/errors";
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import { giftCardDataFragment } from "../queries";
import {
GiftCardResend,
GiftCardResendVariables
} from "./types/GiftCardResend";
const giftCardResend = gql`
${giftCardDataFragment}
${giftCardErrorFragment}
mutation GiftCardResend($input: GiftCardResendInput!) {
giftCardResend(input: $input) {
errors {
...GiftCardError
}
giftCard {
...GiftCardData
}
}
}
`;
export const useGiftCardResendCodeMutation = makeMutation<
GiftCardResend,
GiftCardResendVariables
>(giftCardResend);

View file

@ -0,0 +1,102 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardResendInput, GiftCardErrorCode } from "./../../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: GiftCardResend
// ====================================================
export interface GiftCardResend_giftCardResend_errors {
__typename: "GiftCardError";
code: GiftCardErrorCode;
field: string | null;
}
export interface GiftCardResend_giftCardResend_giftCard_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface GiftCardResend_giftCardResend_giftCard_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface GiftCardResend_giftCardResend_giftCard_createdBy {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardResend_giftCardResend_giftCard_product {
__typename: "Product";
id: string;
name: string;
}
export interface GiftCardResend_giftCardResend_giftCard_usedBy {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardResend_giftCardResend_giftCard_app {
__typename: "App";
id: string;
name: string | null;
}
export interface GiftCardResend_giftCardResend_giftCard_initialBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardResend_giftCardResend_giftCard_currentBalance {
__typename: "Money";
amount: number;
currency: string;
}
export interface GiftCardResend_giftCardResend_giftCard {
__typename: "GiftCard";
metadata: (GiftCardResend_giftCardResend_giftCard_metadata | null)[];
privateMetadata: (GiftCardResend_giftCardResend_giftCard_privateMetadata | null)[];
displayCode: string;
boughtInChannel: string | null;
createdBy: GiftCardResend_giftCardResend_giftCard_createdBy | null;
product: GiftCardResend_giftCardResend_giftCard_product | null;
usedBy: GiftCardResend_giftCardResend_giftCard_usedBy | null;
usedByEmail: string | null;
createdByEmail: string | null;
app: GiftCardResend_giftCardResend_giftCard_app | null;
created: any;
expiryDate: any | null;
lastUsedOn: any | null;
isActive: boolean;
initialBalance: GiftCardResend_giftCardResend_giftCard_initialBalance | null;
currentBalance: GiftCardResend_giftCardResend_giftCard_currentBalance | null;
id: string;
tag: string | null;
}
export interface GiftCardResend_giftCardResend {
__typename: "GiftCardResend";
errors: GiftCardResend_giftCardResend_errors[];
giftCard: GiftCardResend_giftCardResend_giftCard | null;
}
export interface GiftCardResend {
giftCardResend: GiftCardResend_giftCardResend | null;
}
export interface GiftCardResendVariables {
input: GiftCardResendInput;
}

View file

@ -0,0 +1,34 @@
import { UserError } from "@saleor/types";
import { FormErrors, getFormErrors } from "@saleor/utils/errors";
import { useEffect, useState } from "react";
export function useDialogFormReset<
TError extends UserError,
TKey extends string
>({
reset,
apiErrors,
keys,
open
}: {
reset: () => void;
apiErrors: TError[];
keys: TKey[];
open: boolean;
}) {
const [formErrors, setFormErrors] = useState<FormErrors<TKey, TError>>(null);
useEffect(() => {
if (!open) {
setFormErrors(null);
reset();
}
}, [open]);
useEffect(() => {
const errors = getFormErrors(keys, apiErrors);
setFormErrors(errors);
}, [apiErrors]);
return { formErrors };
}

View file

@ -1,16 +1,16 @@
import { TextField, Typography } from "@material-ui/core"; import { TextField, Typography } from "@material-ui/core";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import Form from "@saleor/components/Form";
import { IMessage } from "@saleor/components/messages"; import { IMessage } from "@saleor/components/messages";
import useForm from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { getFormErrors } from "@saleor/utils/errors";
import commonErrorMessages from "@saleor/utils/errors/common"; import commonErrorMessages from "@saleor/utils/errors/common";
import { DialogActionHandlers } from "@saleor/utils/handlers/dialogActionHandlers"; import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { giftCardsListTableMessages as tableMessages } from "../../GiftCardsList/messages"; import { giftCardsListTableMessages as tableMessages } from "../../GiftCardsList/messages";
import { useDialogFormReset } from "../GiftCardResendCodeDialog/utils";
import { getGiftCardErrorMessage } from "../messages"; import { getGiftCardErrorMessage } from "../messages";
import { useGiftCardUpdateMutation } from "../mutations"; import { useGiftCardUpdateMutation } from "../mutations";
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
@ -22,9 +22,9 @@ export interface GiftCardBalanceUpdateFormData {
balanceAmount: number; balanceAmount: number;
} }
const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlers> = ({ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlersProps> = ({
open, open,
onClose closeDialog
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
@ -57,7 +57,7 @@ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlers> = ({
notify(notifierData); notify(notifierData);
if (!errors.length) { if (!errors.length) {
onClose(); closeDialog();
} }
}; };
@ -83,22 +83,27 @@ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlers> = ({
return result?.data?.giftCardUpdate?.errors; return result?.data?.giftCardUpdate?.errors;
}; };
const { loading, status, data } = updateGiftCardBalanceOpts; const { data, change, submit, reset, hasChanged } = useForm(
initialFormData,
const formErrors = getFormErrors( handleSubmit
["initialBalanceAmount"],
data?.giftCardUpdate?.errors
); );
const { loading, status, data: submitData } = updateGiftCardBalanceOpts;
const { formErrors } = useDialogFormReset({
open,
reset,
keys: ["initialBalanceAmount"],
apiErrors: submitData?.giftCardUpdate?.errors
});
return ( return (
<Form initial={initialFormData} onSubmit={handleSubmit}>
{({ data, change, submit, hasChanged }) => (
<ActionDialog <ActionDialog
maxWidth="sm" maxWidth="sm"
open={open} open={open}
onConfirm={submit} onConfirm={submit}
confirmButtonLabel={intl.formatMessage(messages.changeButtonLabel)} confirmButtonLabel={intl.formatMessage(messages.changeButtonLabel)}
onClose={onClose} onClose={closeDialog}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
confirmButtonState={status} confirmButtonState={status}
disabled={loading || !hasChanged} disabled={loading || !hasChanged}
@ -119,7 +124,7 @@ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlers> = ({
label={intl.formatMessage( label={intl.formatMessage(
tableMessages.giftCardsTableColumnBalanceTitle tableMessages.giftCardsTableColumnBalanceTitle
)} )}
type="number" type="float"
InputProps={{ InputProps={{
startAdornment: ( startAdornment: (
<div className={classes.currencyCodeContainer}> <div className={classes.currencyCodeContainer}>
@ -129,8 +134,6 @@ const GiftCardUpdateBalanceDialog: React.FC<DialogActionHandlers> = ({
}} }}
/> />
</ActionDialog> </ActionDialog>
)}
</Form>
); );
}; };

View file

@ -2,8 +2,8 @@ import { Button, Card, CardContent, Divider } from "@material-ui/core";
import CardSpacer from "@saleor/components/CardSpacer"; import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle"; import CardTitle from "@saleor/components/CardTitle";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import GiftCardExpirySelect from "@saleor/giftCards/components/GiftCardExpirySelect";
import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput"; import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput";
import GiftCardUpdateExpirySelect from "@saleor/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -21,7 +21,7 @@ const GiftCardUpdateDetailsCard: React.FC = () => {
const { const {
change, change,
data: { expiryType, expiryPeriodAmount, expiryPeriodType, tag, expiryDate }, data: { tag },
formErrors formErrors
} = useGiftCardUpdateForm(); } = useGiftCardUpdateForm();
@ -55,14 +55,7 @@ const GiftCardUpdateDetailsCard: React.FC = () => {
change={change} change={change}
/> />
<CardSpacer /> <CardSpacer />
<GiftCardExpirySelect <GiftCardUpdateExpirySelect />
expiryDate={expiryDate}
errors={formErrors}
change={change}
expiryType={expiryType}
expiryPeriodAmount={expiryPeriodAmount}
expiryPeriodType={expiryPeriodType}
/>
</> </>
)} )}
</Skeleton> </Skeleton>

View file

@ -0,0 +1,69 @@
import { TextField, Typography } from "@material-ui/core";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { getGiftCardErrorMessage } from "@saleor/giftCards/GiftCardUpdate/messages";
import useGiftCardUpdateForm from "@saleor/giftCards/GiftCardUpdate/providers/GiftCardUpdateFormProvider/hooks/useGiftCardUpdateForm";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
import React, { useEffect } from "react";
import { useIntl } from "react-intl";
import { giftCardExpirySelectMessages as messages } from "./messages";
import { useGiftCardExpirySelectStyles as useStyles } from "./styles";
const GiftCardUpdateExpirySelect: React.FC = () => {
const intl = useIntl();
const classes = useStyles({});
const {
change,
data: { expiryDate },
formErrors
} = useGiftCardUpdateForm();
const [cardExpiresSelected, setCardExpiresSelected] = useStateFromProps(
!!expiryDate
);
useEffect(() => {
if (!cardExpiresSelected) {
change({
target: {
name: "expiryDate",
value: null
}
});
}
}, [cardExpiresSelected]);
return (
<>
<Typography>{intl.formatMessage(messages.expiryDateLabel)}</Typography>
<VerticalSpacer />
<ControlledCheckbox
name="cardExpires"
label={intl.formatMessage(messages.expiryDateCheckboxLabel)}
checked={cardExpiresSelected}
onChange={event => setCardExpiresSelected(event.target.value)}
/>
<VerticalSpacer spacing={2} />
{cardExpiresSelected && (
<TextField
error={!!formErrors?.expiryDate}
helperText={getGiftCardErrorMessage(formErrors?.expiryDate, intl)}
onChange={change}
name={"expiryDate"}
className={classes.dateField}
label={intl.formatMessage(messages.expiryDateLabel)}
value={expiryDate}
InputLabelProps={{
shrink: true
}}
type="date"
/>
)}
</>
);
};
export default GiftCardUpdateExpirySelect;

View file

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

View file

@ -0,0 +1,12 @@
import { defineMessages } from "react-intl";
export const giftCardExpirySelectMessages = defineMessages({
expiryDateCheckboxLabel: {
defaultMessage: "Gift card expires",
description: "GiftCarUpdateDetailsExpirySection expiry date checkbox label"
},
expiryDateLabel: {
defaultMessage: "Expiration date",
description: "GiftCarUpdateDetailsExpirySection expiry date label"
}
});

View file

@ -1,13 +1,19 @@
import { makeStyles } from "@saleor/macaw-ui"; import { makeStyles } from "@saleor/macaw-ui";
export const useGiftCardExpirySelectStyles = makeStyles( export const useGiftCardExpirySelectStyles = makeStyles(
() => ({ theme => ({
radioGroupContainer: { radioGroupContainer: {
display: "flex", display: "flex",
flexDirection: "row" flexDirection: "row"
}, },
dateField: { dateField: {
width: 400 width: 400
},
periodField: {
display: "flex"
},
dateText: {
marginTop: theme.spacing(0.5)
} }
}), }),
{ name: "GiftCardUpdateDetailsExpirySection" } { name: "GiftCardUpdateDetailsExpirySection" }

View file

@ -8,6 +8,7 @@ import useNavigator from "@saleor/hooks/useNavigator";
import { getFullName, getStringOrPlaceholder } from "@saleor/misc"; import { getFullName, getStringOrPlaceholder } from "@saleor/misc";
import Label from "@saleor/orders/components/OrderHistory/Label"; import Label from "@saleor/orders/components/OrderHistory/Label";
import { getOrderNumberLinkObject } from "@saleor/orders/components/OrderHistory/utils"; import { getOrderNumberLinkObject } from "@saleor/orders/components/OrderHistory/utils";
import { getByType } from "@saleor/orders/components/OrderReturnPage/utils";
import { productUrl } from "@saleor/products/urls"; import { productUrl } from "@saleor/products/urls";
import { staffMemberDetailsUrl } from "@saleor/staff/urls"; import { staffMemberDetailsUrl } from "@saleor/staff/urls";
import { GiftCardEventsEnum } from "@saleor/types/globalTypes"; import { GiftCardEventsEnum } from "@saleor/types/globalTypes";
@ -37,9 +38,8 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
events events
} = giftCard; } = giftCard;
const cardIssuedEvent = events.find( const cardIssuedEvent = events.find(getByType(GiftCardEventsEnum.ISSUED));
({ type }) => type === GiftCardEventsEnum.ISSUED const cardBoughtEvent = events.find(getByType(GiftCardEventsEnum.BOUGHT));
);
const getBuyerFieldData = (): { const getBuyerFieldData = (): {
label: MessageDescriptor; label: MessageDescriptor;
@ -80,13 +80,31 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
}; };
}; };
const orderData = const getOrderData = () => {
cardIssuedEvent && cardIssuedEvent.orderId if (cardIssuedEvent) {
? getOrderNumberLinkObject({ const { orderId, orderNumber } = cardIssuedEvent;
id: cardIssuedEvent.orderId,
number: cardIssuedEvent.orderNumber if (!orderId) {
}) return null;
: null; }
return getOrderNumberLinkObject({
id: orderId,
number: orderNumber
});
}
if (cardBoughtEvent) {
const { orderId, orderNumber } = cardBoughtEvent;
return getOrderNumberLinkObject({
id: orderId,
number: orderNumber
});
}
return null;
};
const { const {
label: buyerLabelMessage, label: buyerLabelMessage,
@ -94,6 +112,8 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
url: buyerUrl url: buyerUrl
} = getBuyerFieldData(); } = getBuyerFieldData();
const orderData = getOrderData();
return ( return (
<> <>
<Label text={intl.formatMessage(messages.creationLabel)} /> <Label text={intl.formatMessage(messages.creationLabel)} />

View file

@ -13,7 +13,7 @@ import useGiftCardUpdate from "./providers/GiftCardUpdateFormProvider/hooks/useG
import useGiftCardUpdateForm from "./providers/GiftCardUpdateFormProvider/hooks/useGiftCardUpdateForm"; import useGiftCardUpdateForm from "./providers/GiftCardUpdateFormProvider/hooks/useGiftCardUpdateForm";
const GiftCardUpdatePage: React.FC = () => { const GiftCardUpdatePage: React.FC = () => {
const { navigateBack } = useGiftCardUpdateDialogs(); const { navigateBack, openDeleteDialog } = useGiftCardUpdateDialogs();
const { const {
hasChanged, hasChanged,
@ -44,6 +44,7 @@ const GiftCardUpdatePage: React.FC = () => {
disabled={loadingUpdate || !hasChanged} disabled={loadingUpdate || !hasChanged}
onCancel={navigateBack} onCancel={navigateBack}
onSubmit={submit} onSubmit={submit}
onDelete={openDeleteDialog}
/> />
</Container> </Container>
); );

View file

@ -2,22 +2,20 @@ import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import { ConfirmButton } from "@saleor/macaw-ui"; import { ConfirmButton } from "@saleor/macaw-ui";
import commonErrorMessages from "@saleor/utils/errors/common"; import commonErrorMessages from "@saleor/utils/errors/common";
import classNames from "classnames"; import React from "react";
import React, { useEffect, useState } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { bulkEnableDisableSectionMessages as buttonMessages } from "../../GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages";
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
import { giftCardEnableDisableSectionMessages as messages } from "./messages"; import { giftCardEnableDisableSectionMessages as messages } from "./messages";
import { import {
useGiftCardActivateMutation, useGiftCardActivateMutation,
useGiftCardDeactivateMutation useGiftCardDeactivateMutation
} from "./mutations"; } from "./mutations";
import { useGiftCardEnableDisableSectionStyles as useStyles } from "./styles";
import { GiftCardActivate } from "./types/GiftCardActivate"; import { GiftCardActivate } from "./types/GiftCardActivate";
import { GiftCardDeactivate } from "./types/GiftCardDeactivate"; import { GiftCardDeactivate } from "./types/GiftCardDeactivate";
const GiftCardEnableDisableSection: React.FC = () => { const GiftCardEnableDisableSection: React.FC = () => {
const classes = useStyles({});
const notify = useNotifier(); const notify = useNotifier();
const intl = useIntl(); const intl = useIntl();
@ -25,10 +23,6 @@ const GiftCardEnableDisableSection: React.FC = () => {
giftCard: { id, isActive } giftCard: { id, isActive }
} = useGiftCardDetails(); } = useGiftCardDetails();
const [showButtonGreen, setShowButtonGreen] = useState(!isActive);
useEffect(() => setShowButtonGreen(!isActive), [isActive]);
const onActivateCompleted = (data: GiftCardActivate) => { const onActivateCompleted = (data: GiftCardActivate) => {
const errors = data?.giftCardActivate?.errors; const errors = data?.giftCardActivate?.errors;
@ -38,8 +32,6 @@ const GiftCardEnableDisableSection: React.FC = () => {
text: intl.formatMessage(commonErrorMessages.unknownError) text: intl.formatMessage(commonErrorMessages.unknownError)
}); });
setShowButtonGreen(false);
setTimeout(() => setShowButtonGreen(true), 3000);
return; return;
} }
@ -82,16 +74,14 @@ const GiftCardEnableDisableSection: React.FC = () => {
? giftCardDeactivate({ variables: { id } }) ? giftCardDeactivate({ variables: { id } })
: giftCardActivate({ variables: { id } }); : giftCardActivate({ variables: { id } });
const buttonLabel = isActive ? messages.disableLabel : messages.enableLabel; const buttonLabel = isActive
? buttonMessages.disableLabel
: buttonMessages.enableLabel;
const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts; const currentOpts = isActive ? giftCardDeactivateOpts : giftCardActivateOpts;
return ( return (
<ConfirmButton <ConfirmButton
className={classNames(classes.button, {
[classes.buttonRed]: isActive || currentOpts?.status === "error",
[classes.buttonGreen]: showButtonGreen
})}
onClick={handleClick} onClick={handleClick}
transitionState={currentOpts?.status} transitionState={currentOpts?.status}
labels={{ labels={{

View file

@ -1,3 +1,5 @@
import { Button } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import PageHeader from "@saleor/components/PageHeader"; import PageHeader from "@saleor/components/PageHeader";
import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip"; import PageTitleWithStatusChip from "@saleor/components/PageTitleWithStatusChip";
import { StatusType } from "@saleor/components/StatusChip/types"; import { StatusType } from "@saleor/components/StatusChip/types";
@ -10,6 +12,7 @@ import { giftCardsListTableMessages as tableMessages } from "../../GiftCardsList
import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails";
import useGiftCardUpdateDialogs from "../providers/GiftCardUpdateDialogsProvider/hooks/useGiftCardUpdateDialogs"; import useGiftCardUpdateDialogs from "../providers/GiftCardUpdateDialogsProvider/hooks/useGiftCardUpdateDialogs";
import GiftCardEnableDisableSection from "./GiftCardEnableDisableSection"; import GiftCardEnableDisableSection from "./GiftCardEnableDisableSection";
import { giftCardUpdatePageHeaderMessages as messages } from "./messages";
const GiftCardUpdatePageHeader: React.FC = () => { const GiftCardUpdatePageHeader: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
@ -20,6 +23,8 @@ const GiftCardUpdatePageHeader: React.FC = () => {
return null; return null;
} }
const { openResendCodeDialog } = useGiftCardUpdateDialogs();
const { displayCode, isActive } = giftCard; const { displayCode, isActive } = giftCard;
const title = intl.formatMessage(tableMessages.codeEndingWithLabel, { const title = intl.formatMessage(tableMessages.codeEndingWithLabel, {
@ -48,6 +53,14 @@ const GiftCardUpdatePageHeader: React.FC = () => {
} }
> >
<GiftCardEnableDisableSection /> <GiftCardEnableDisableSection />
<HorizontalSpacer />
<Button
color="primary"
variant="contained"
onClick={openResendCodeDialog}
>
{intl.formatMessage(messages.resendButtonLabel)}
</Button>
</PageHeader> </PageHeader>
</> </>
); );

View file

@ -1,14 +1,6 @@
import { defineMessages } from "react-intl"; import { defineMessages } from "react-intl";
export const giftCardEnableDisableSectionMessages = defineMessages({ export const giftCardEnableDisableSectionMessages = defineMessages({
enableLabel: {
defaultMessage: "Enable",
description: "GiftCardEnableDisableSection enable label"
},
disableLabel: {
defaultMessage: "Disable",
description: "GiftCardEnableDisableSection enable label"
},
successfullyEnabledTitle: { successfullyEnabledTitle: {
defaultMessage: "Successfully enabled gift card", defaultMessage: "Successfully enabled gift card",
description: "GiftCardEnableDisableSection enable success" description: "GiftCardEnableDisableSection enable success"
@ -18,3 +10,10 @@ export const giftCardEnableDisableSectionMessages = defineMessages({
description: "GiftCardEnableDisableSection disable success" description: "GiftCardEnableDisableSection disable success"
} }
}); });
export const giftCardUpdatePageHeaderMessages = defineMessages({
resendButtonLabel: {
defaultMessage: "Resend code",
description: "giftCardUpdatePageHeader resendButtonLabel"
}
});

View file

@ -1,27 +0,0 @@
import { darken } from "@material-ui/core";
import { statusChipStyles } from "@saleor/components/StatusChip/StatusChip";
import { makeStyles } from "@saleor/macaw-ui";
export const useGiftCardEnableDisableSectionStyles = makeStyles(
theme => ({
button: {
transition: "backgroundColor 0ms"
},
buttonRed: {
backgroundColor: theme.palette.error.main,
color: "#ffffff",
"&:hover": {
backgroundColor: darken(theme.palette.error.main, 0.1)
}
},
buttonGreen: {
backgroundColor: statusChipStyles.successLabel.color,
"&:hover": {
backgroundColor: darken(statusChipStyles.successLabel.color, 0.1)
}
}
}),
{ name: "GiftCardEnableDisableSection" }
);

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { GiftCardErrorCode, GiftCardExpiryTypeEnum, TimePeriodTypeEnum } from "./../../../../types/globalTypes"; import { GiftCardErrorCode } from "./../../../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: GiftCardActivate // GraphQL mutation operation: GiftCardActivate
@ -40,13 +40,6 @@ export interface GiftCardActivate_giftCardActivate_giftCard_product {
name: string; name: string;
} }
export interface GiftCardActivate_giftCardActivate_giftCard_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardActivate_giftCardActivate_giftCard_usedBy { export interface GiftCardActivate_giftCardActivate_giftCard_usedBy {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -60,12 +53,6 @@ export interface GiftCardActivate_giftCardActivate_giftCard_app {
name: string | null; name: string | null;
} }
export interface GiftCardActivate_giftCardActivate_giftCard_expiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardActivate_giftCardActivate_giftCard_initialBalance { export interface GiftCardActivate_giftCardActivate_giftCard_initialBalance {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -83,17 +70,15 @@ export interface GiftCardActivate_giftCardActivate_giftCard {
metadata: (GiftCardActivate_giftCardActivate_giftCard_metadata | null)[]; metadata: (GiftCardActivate_giftCardActivate_giftCard_metadata | null)[];
privateMetadata: (GiftCardActivate_giftCardActivate_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardActivate_giftCardActivate_giftCard_privateMetadata | null)[];
displayCode: string; displayCode: string;
boughtInChannel: string | null;
createdBy: GiftCardActivate_giftCardActivate_giftCard_createdBy | null; createdBy: GiftCardActivate_giftCardActivate_giftCard_createdBy | null;
product: GiftCardActivate_giftCardActivate_giftCard_product | null; product: GiftCardActivate_giftCardActivate_giftCard_product | null;
user: GiftCardActivate_giftCardActivate_giftCard_user | null;
usedBy: GiftCardActivate_giftCardActivate_giftCard_usedBy | null; usedBy: GiftCardActivate_giftCardActivate_giftCard_usedBy | null;
usedByEmail: string | null; usedByEmail: string | null;
createdByEmail: string | null; createdByEmail: string | null;
app: GiftCardActivate_giftCardActivate_giftCard_app | null; app: GiftCardActivate_giftCardActivate_giftCard_app | null;
created: any; created: any;
expiryDate: any | null; expiryDate: any | null;
expiryType: GiftCardExpiryTypeEnum;
expiryPeriod: GiftCardActivate_giftCardActivate_giftCard_expiryPeriod | null;
lastUsedOn: any | null; lastUsedOn: any | null;
isActive: boolean; isActive: boolean;
initialBalance: GiftCardActivate_giftCardActivate_giftCard_initialBalance | null; initialBalance: GiftCardActivate_giftCardActivate_giftCard_initialBalance | null;

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { GiftCardErrorCode, GiftCardExpiryTypeEnum, TimePeriodTypeEnum } from "./../../../../types/globalTypes"; import { GiftCardErrorCode } from "./../../../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: GiftCardDeactivate // GraphQL mutation operation: GiftCardDeactivate
@ -40,13 +40,6 @@ export interface GiftCardDeactivate_giftCardDeactivate_giftCard_product {
name: string; name: string;
} }
export interface GiftCardDeactivate_giftCardDeactivate_giftCard_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardDeactivate_giftCardDeactivate_giftCard_usedBy { export interface GiftCardDeactivate_giftCardDeactivate_giftCard_usedBy {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -60,12 +53,6 @@ export interface GiftCardDeactivate_giftCardDeactivate_giftCard_app {
name: string | null; name: string | null;
} }
export interface GiftCardDeactivate_giftCardDeactivate_giftCard_expiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardDeactivate_giftCardDeactivate_giftCard_initialBalance { export interface GiftCardDeactivate_giftCardDeactivate_giftCard_initialBalance {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -83,17 +70,15 @@ export interface GiftCardDeactivate_giftCardDeactivate_giftCard {
metadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_metadata | null)[]; metadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_metadata | null)[];
privateMetadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardDeactivate_giftCardDeactivate_giftCard_privateMetadata | null)[];
displayCode: string; displayCode: string;
boughtInChannel: string | null;
createdBy: GiftCardDeactivate_giftCardDeactivate_giftCard_createdBy | null; createdBy: GiftCardDeactivate_giftCardDeactivate_giftCard_createdBy | null;
product: GiftCardDeactivate_giftCardDeactivate_giftCard_product | null; product: GiftCardDeactivate_giftCardDeactivate_giftCard_product | null;
user: GiftCardDeactivate_giftCardDeactivate_giftCard_user | null;
usedBy: GiftCardDeactivate_giftCardDeactivate_giftCard_usedBy | null; usedBy: GiftCardDeactivate_giftCardDeactivate_giftCard_usedBy | null;
usedByEmail: string | null; usedByEmail: string | null;
createdByEmail: string | null; createdByEmail: string | null;
app: GiftCardDeactivate_giftCardDeactivate_giftCard_app | null; app: GiftCardDeactivate_giftCardDeactivate_giftCard_app | null;
created: any; created: any;
expiryDate: any | null; expiryDate: any | null;
expiryType: GiftCardExpiryTypeEnum;
expiryPeriod: GiftCardDeactivate_giftCardDeactivate_giftCard_expiryPeriod | null;
lastUsedOn: any | null; lastUsedOn: any | null;
isActive: boolean; isActive: boolean;
initialBalance: GiftCardDeactivate_giftCardDeactivate_giftCard_initialBalance | null; initialBalance: GiftCardDeactivate_giftCardDeactivate_giftCard_initialBalance | null;

View file

@ -1,10 +1,8 @@
import { commonMessages } from "@saleor/intl"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { GiftCardErrorCode } from "@saleor/types/globalTypes"; import { GiftCardErrorCode } from "@saleor/types/globalTypes";
import commonErrorMessages from "@saleor/utils/errors/common"; import { getCommonFormFieldErrorMessage } from "@saleor/utils/errors/common";
import { defineMessages, IntlShape } from "react-intl"; import { defineMessages, IntlShape } from "react-intl";
import { GiftCardUpdate_giftCardUpdate_errors } from "./types/GiftCardUpdate";
export const giftCardUpdateDetailsCardMessages = defineMessages({ export const giftCardUpdateDetailsCardMessages = defineMessages({
title: { title: {
defaultMessage: "Details", defaultMessage: "Details",
@ -12,23 +10,23 @@ export const giftCardUpdateDetailsCardMessages = defineMessages({
} }
}); });
const giftCardErrorMessages = defineMessages({
notFound: {
defaultMessage: "Couldn't find gift card",
description: "giftCardErrorMessages not found"
}
});
export function getGiftCardErrorMessage( export function getGiftCardErrorMessage(
error: Omit<GiftCardUpdate_giftCardUpdate_errors, "__typename"> | undefined, error: Omit<GiftCardError, "__typename"> | undefined,
intl: IntlShape intl: IntlShape
): string { ): string {
if (error) { if (error) {
switch (error.code) { switch (error.code) {
case GiftCardErrorCode.GRAPHQL_ERROR: case GiftCardErrorCode.NOT_FOUND:
return intl.formatMessage(commonErrorMessages.graphqlError); return intl.formatMessage(giftCardErrorMessages.notFound);
case GiftCardErrorCode.REQUIRED:
return intl.formatMessage(commonMessages.requiredField);
case GiftCardErrorCode.INVALID:
return intl.formatMessage(commonErrorMessages.invalid);
default:
return intl.formatMessage(commonErrorMessages.unknownError);
} }
} }
return undefined; return getCommonFormFieldErrorMessage(error, intl);
} }

View file

@ -1,8 +1,10 @@
import GiftCardUpdatePageDeleteDialog from "@saleor/giftCards/components/GiftCardDeleteDialog/GiftCardUpdatePageDeleteDialog";
import { giftCardsListPath, giftCardUrl } from "@saleor/giftCards/urls"; import { giftCardsListPath, giftCardUrl } from "@saleor/giftCards/urls";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import React, { createContext } from "react"; import React, { createContext } from "react";
import GiftCardResendCodeDialog from "../../GiftCardResendCodeDialog";
import GiftCardUpdateBalanceDialog from "../../GiftCardUpdateBalanceDialog"; import GiftCardUpdateBalanceDialog from "../../GiftCardUpdateBalanceDialog";
import { import {
GiftCardUpdatePageActionParamsEnum, GiftCardUpdatePageActionParamsEnum,
@ -18,8 +20,10 @@ interface GiftCardUpdateDialogsProviderProps {
export interface GiftCardUpdateDialogsConsumerProps { export interface GiftCardUpdateDialogsConsumerProps {
navigateBack: () => void; navigateBack: () => void;
openSetBalanceDialog: () => void;
closeDialog: () => void; closeDialog: () => void;
openSetBalanceDialog: () => void;
openDeleteDialog: () => void;
openResendCodeDialog: () => void;
} }
export const GiftCardUpdateDialogsContext = createContext< export const GiftCardUpdateDialogsContext = createContext<
@ -35,21 +39,26 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
const { loading: loadingGiftCard } = useGiftCardDetails(); const { loading: loadingGiftCard } = useGiftCardDetails();
const {
SET_BALANCE,
DELETE,
RESEND_CODE
} = GiftCardUpdatePageActionParamsEnum;
const [openDialog, closeDialog] = createDialogActionHandlers< const [openDialog, closeDialog] = createDialogActionHandlers<
GiftCardUpdatePageActionParamsEnum, GiftCardUpdatePageActionParamsEnum,
GiftCardUpdatePageUrlQueryParams GiftCardUpdatePageUrlQueryParams
>(navigate, params => giftCardUrl(id, params), params); >(navigate, params => giftCardUrl(id, params), params);
const openSetBalanceDialog = () => const isDialogOpen = (action: GiftCardUpdatePageActionParamsEnum) =>
openDialog(GiftCardUpdatePageActionParamsEnum.SET_BALANCE); params?.action === action;
const isSetBalanceDialogOpen =
params?.action === GiftCardUpdatePageActionParamsEnum.SET_BALANCE;
const navigateBack = () => navigate(giftCardsListPath); const navigateBack = () => navigate(giftCardsListPath);
const providerValues: GiftCardUpdateDialogsConsumerProps = { const providerValues: GiftCardUpdateDialogsConsumerProps = {
openSetBalanceDialog, openSetBalanceDialog: () => openDialog(SET_BALANCE),
openDeleteDialog: () => openDialog(DELETE),
openResendCodeDialog: () => openDialog(RESEND_CODE),
closeDialog, closeDialog,
navigateBack navigateBack
}; };
@ -58,10 +67,21 @@ const GiftCardUpdateDialogsProvider: React.FC<GiftCardUpdateDialogsProviderProps
<GiftCardUpdateDialogsContext.Provider value={providerValues}> <GiftCardUpdateDialogsContext.Provider value={providerValues}>
{children} {children}
{!loadingGiftCard && ( {!loadingGiftCard && (
<>
<GiftCardUpdateBalanceDialog <GiftCardUpdateBalanceDialog
onClose={closeDialog} closeDialog={closeDialog}
open={isSetBalanceDialogOpen} open={isDialogOpen(SET_BALANCE)}
/> />
<GiftCardUpdatePageDeleteDialog
closeDialog={closeDialog}
open={isDialogOpen(DELETE)}
navigateBack={navigateBack}
/>
<GiftCardResendCodeDialog
open={isDialogOpen(RESEND_CODE)}
closeDialog={closeDialog}
/>
</>
)} )}
</GiftCardUpdateDialogsContext.Provider> </GiftCardUpdateDialogsContext.Provider>
); );

View file

@ -1,12 +1,10 @@
import { MetadataFormData } from "@saleor/components/Metadata"; import { MetadataFormData } from "@saleor/components/Metadata";
import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError";
import { GiftCardCommonFormData } from "@saleor/giftCards/GiftCardCreateDialog/types"; import { GiftCardCommonFormData } from "@saleor/giftCards/GiftCardCreateDialog/types";
import { getGiftCardExpirySettingsInputData } from "@saleor/giftCards/GiftCardCreateDialog/utils";
import { MutationResultWithOpts } from "@saleor/hooks/makeMutation"; import { MutationResultWithOpts } from "@saleor/hooks/makeMutation";
import useForm, { FormChange, UseFormResult } from "@saleor/hooks/useForm"; import useForm, { FormChange, UseFormResult } from "@saleor/hooks/useForm";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { getDefaultNotifierSuccessErrorData } from "@saleor/hooks/useNotifier/utils"; import { getDefaultNotifierSuccessErrorData } from "@saleor/hooks/useNotifier/utils";
import { TimePeriodTypeEnum } from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors"; import { getFormErrors } from "@saleor/utils/errors";
import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit"; import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit";
import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler";
@ -38,7 +36,7 @@ export interface GiftCardUpdateFormConsumerData
} }
export interface GiftCardUpdateFormErrors { export interface GiftCardUpdateFormErrors {
formErrors: Record<"tag" | "expiryDate" | "expiryPeriod", GiftCardError>; formErrors: Record<"tag" | "expiryDate", GiftCardError>;
handlers: { changeMetadata: FormChange }; handlers: { changeMetadata: FormChange };
} }
@ -66,21 +64,11 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
return { ...emptyFormData, metadata: [], privateMetadata: [] }; return { ...emptyFormData, metadata: [], privateMetadata: [] };
} }
const { const { tag, expiryDate, privateMetadata, metadata } = giftCard;
tag,
expiryDate,
expiryType,
expiryPeriod,
privateMetadata,
metadata
} = giftCard;
return { return {
tag, tag,
expiryDate, expiryDate,
expiryType,
expiryPeriodType: expiryPeriod?.type || TimePeriodTypeEnum.YEAR,
expiryPeriodAmount: expiryPeriod?.amount || 1,
privateMetadata: privateMetadata?.map(mapMetadataItemToInput), privateMetadata: privateMetadata?.map(mapMetadataItemToInput),
metadata: metadata?.map(mapMetadataItemToInput) metadata: metadata?.map(mapMetadataItemToInput)
}; };
@ -96,13 +84,13 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
onCompleted: onSubmit onCompleted: onSubmit
}); });
const submit = async (formData: GiftCardUpdateFormData) => { const submit = async ({ tag, expiryDate }: GiftCardUpdateFormData) => {
const result = await updateGiftCard({ const result = await updateGiftCard({
variables: { variables: {
id: giftCard?.id, id: giftCard?.id,
input: { input: {
tag: formData.tag, tag,
expirySettings: getGiftCardExpirySettingsInputData(formData) expiryDate
} }
} }
}); });
@ -138,7 +126,7 @@ const GiftCardUpdateFormProvider: React.FC<GiftCardUpdateFormProviderProps> = ({
handleFormSubmit(submitData, handleSubmit, setChanged); handleFormSubmit(submitData, handleSubmit, setChanged);
const formErrors = getFormErrors( const formErrors = getFormErrors(
["tag", "expiryDate", "expiryPeriod"], ["tag", "expiryDate"],
updateGiftCardOpts?.data?.giftCardUpdate?.errors updateGiftCardOpts?.data?.giftCardUpdate?.errors
); );

View file

@ -1,7 +1,6 @@
import { fragmentUserBase } from "@saleor/fragments/auth"; import { fragmentUserBase } from "@saleor/fragments/auth";
import { metadataFragment } from "@saleor/fragments/metadata"; import { metadataFragment } from "@saleor/fragments/metadata";
import { fragmentMoney } from "@saleor/fragments/products"; import { fragmentMoney } from "@saleor/fragments/products";
import { fragmentTimePeriod } from "@saleor/fragments/timePeriod";
import makeQuery from "@saleor/hooks/makeQuery"; import makeQuery from "@saleor/hooks/makeQuery";
import gql from "graphql-tag"; import gql from "graphql-tag";
@ -14,10 +13,10 @@ export const giftCardDataFragment = gql`
${fragmentMoney} ${fragmentMoney}
${metadataFragment} ${metadataFragment}
${fragmentUserBase} ${fragmentUserBase}
${fragmentTimePeriod}
fragment GiftCardData on GiftCard { fragment GiftCardData on GiftCard {
...MetadataFragment ...MetadataFragment
displayCode displayCode
boughtInChannel
createdBy { createdBy {
...UserBase ...UserBase
} }
@ -25,7 +24,7 @@ export const giftCardDataFragment = gql`
id id
name name
} }
user { createdBy {
...UserBase ...UserBase
} }
usedBy { usedBy {
@ -39,10 +38,6 @@ export const giftCardDataFragment = gql`
} }
created created
expiryDate expiryDate
expiryType
expiryPeriod {
...TimePeriod
}
lastUsedOn lastUsedOn
isActive isActive
initialBalance { initialBalance {
@ -63,18 +58,8 @@ export const giftCardDetails = gql`
giftCard(id: $id) { giftCard(id: $id) {
...GiftCardData ...GiftCardData
events { events {
expiry {
expiryType
expiryPeriod {
...TimePeriod
}
expiryDate expiryDate
oldExpiryType
oldExpiryPeriod {
...TimePeriod
}
oldExpiryDate oldExpiryDate
}
id id
date date
type type

View file

@ -1,7 +1,9 @@
import { Dialog } from "@saleor/types"; import { Dialog } from "@saleor/types";
export enum GiftCardUpdatePageActionParamsEnum { export enum GiftCardUpdatePageActionParamsEnum {
SET_BALANCE = "set-balance" SET_BALANCE = "set-balance",
DELETE = "delete",
RESEND_CODE = "resend-code"
} }
export type GiftCardUpdatePageUrlQueryParams = Dialog< export type GiftCardUpdatePageUrlQueryParams = Dialog<

View file

@ -3,8 +3,6 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { GiftCardExpiryTypeEnum, TimePeriodTypeEnum } from "./../../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: GiftCardData // GraphQL fragment: GiftCardData
// ==================================================== // ====================================================
@ -34,13 +32,6 @@ export interface GiftCardData_product {
name: string; name: string;
} }
export interface GiftCardData_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardData_usedBy { export interface GiftCardData_usedBy {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -54,12 +45,6 @@ export interface GiftCardData_app {
name: string | null; name: string | null;
} }
export interface GiftCardData_expiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardData_initialBalance { export interface GiftCardData_initialBalance {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -77,17 +62,15 @@ export interface GiftCardData {
metadata: (GiftCardData_metadata | null)[]; metadata: (GiftCardData_metadata | null)[];
privateMetadata: (GiftCardData_privateMetadata | null)[]; privateMetadata: (GiftCardData_privateMetadata | null)[];
displayCode: string; displayCode: string;
boughtInChannel: string | null;
createdBy: GiftCardData_createdBy | null; createdBy: GiftCardData_createdBy | null;
product: GiftCardData_product | null; product: GiftCardData_product | null;
user: GiftCardData_user | null;
usedBy: GiftCardData_usedBy | null; usedBy: GiftCardData_usedBy | null;
usedByEmail: string | null; usedByEmail: string | null;
createdByEmail: string | null; createdByEmail: string | null;
app: GiftCardData_app | null; app: GiftCardData_app | null;
created: any; created: any;
expiryDate: any | null; expiryDate: any | null;
expiryType: GiftCardExpiryTypeEnum;
expiryPeriod: GiftCardData_expiryPeriod | null;
lastUsedOn: any | null; lastUsedOn: any | null;
isActive: boolean; isActive: boolean;
initialBalance: GiftCardData_initialBalance | null; initialBalance: GiftCardData_initialBalance | null;

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { GiftCardExpiryTypeEnum, TimePeriodTypeEnum, GiftCardEventsEnum } from "./../../../types/globalTypes"; import { GiftCardEventsEnum } from "./../../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: GiftCardDetails // GraphQL query operation: GiftCardDetails
@ -34,13 +34,6 @@ export interface GiftCardDetails_giftCard_product {
name: string; name: string;
} }
export interface GiftCardDetails_giftCard_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardDetails_giftCard_usedBy { export interface GiftCardDetails_giftCard_usedBy {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -54,12 +47,6 @@ export interface GiftCardDetails_giftCard_app {
name: string | null; name: string | null;
} }
export interface GiftCardDetails_giftCard_expiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardDetails_giftCard_initialBalance { export interface GiftCardDetails_giftCard_initialBalance {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -72,28 +59,6 @@ export interface GiftCardDetails_giftCard_currentBalance {
currency: string; currency: string;
} }
export interface GiftCardDetails_giftCard_events_expiry_expiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardDetails_giftCard_events_expiry_oldExpiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardDetails_giftCard_events_expiry {
__typename: "GiftCardEventExpiry";
expiryType: GiftCardExpiryTypeEnum | null;
expiryPeriod: GiftCardDetails_giftCard_events_expiry_expiryPeriod | null;
expiryDate: any | null;
oldExpiryType: GiftCardExpiryTypeEnum | null;
oldExpiryPeriod: GiftCardDetails_giftCard_events_expiry_oldExpiryPeriod | null;
oldExpiryDate: any | null;
}
export interface GiftCardDetails_giftCard_events_user { export interface GiftCardDetails_giftCard_events_user {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -133,7 +98,7 @@ export interface GiftCardDetails_giftCard_events_balance_oldCurrentBalance {
export interface GiftCardDetails_giftCard_events_balance { export interface GiftCardDetails_giftCard_events_balance {
__typename: "GiftCardEventBalance"; __typename: "GiftCardEventBalance";
initialBalance: GiftCardDetails_giftCard_events_balance_initialBalance; initialBalance: GiftCardDetails_giftCard_events_balance_initialBalance | null;
currentBalance: GiftCardDetails_giftCard_events_balance_currentBalance; currentBalance: GiftCardDetails_giftCard_events_balance_currentBalance;
oldInitialBalance: GiftCardDetails_giftCard_events_balance_oldInitialBalance | null; oldInitialBalance: GiftCardDetails_giftCard_events_balance_oldInitialBalance | null;
oldCurrentBalance: GiftCardDetails_giftCard_events_balance_oldCurrentBalance | null; oldCurrentBalance: GiftCardDetails_giftCard_events_balance_oldCurrentBalance | null;
@ -141,7 +106,8 @@ export interface GiftCardDetails_giftCard_events_balance {
export interface GiftCardDetails_giftCard_events { export interface GiftCardDetails_giftCard_events {
__typename: "GiftCardEvent"; __typename: "GiftCardEvent";
expiry: GiftCardDetails_giftCard_events_expiry | null; expiryDate: any | null;
oldExpiryDate: any | null;
id: string; id: string;
date: any | null; date: any | null;
type: GiftCardEventsEnum | null; type: GiftCardEventsEnum | null;
@ -161,17 +127,15 @@ export interface GiftCardDetails_giftCard {
metadata: (GiftCardDetails_giftCard_metadata | null)[]; metadata: (GiftCardDetails_giftCard_metadata | null)[];
privateMetadata: (GiftCardDetails_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardDetails_giftCard_privateMetadata | null)[];
displayCode: string; displayCode: string;
boughtInChannel: string | null;
createdBy: GiftCardDetails_giftCard_createdBy | null; createdBy: GiftCardDetails_giftCard_createdBy | null;
product: GiftCardDetails_giftCard_product | null; product: GiftCardDetails_giftCard_product | null;
user: GiftCardDetails_giftCard_user | null;
usedBy: GiftCardDetails_giftCard_usedBy | null; usedBy: GiftCardDetails_giftCard_usedBy | null;
usedByEmail: string | null; usedByEmail: string | null;
createdByEmail: string | null; createdByEmail: string | null;
app: GiftCardDetails_giftCard_app | null; app: GiftCardDetails_giftCard_app | null;
created: any; created: any;
expiryDate: any | null; expiryDate: any | null;
expiryType: GiftCardExpiryTypeEnum;
expiryPeriod: GiftCardDetails_giftCard_expiryPeriod | null;
lastUsedOn: any | null; lastUsedOn: any | null;
isActive: boolean; isActive: boolean;
initialBalance: GiftCardDetails_giftCard_initialBalance | null; initialBalance: GiftCardDetails_giftCard_initialBalance | null;

View file

@ -3,7 +3,7 @@
// @generated // @generated
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { GiftCardUpdateInput, GiftCardErrorCode, GiftCardExpiryTypeEnum, TimePeriodTypeEnum } from "./../../../types/globalTypes"; import { GiftCardUpdateInput, GiftCardErrorCode } from "./../../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: GiftCardUpdate // GraphQL mutation operation: GiftCardUpdate
@ -40,13 +40,6 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard_product {
name: string; name: string;
} }
export interface GiftCardUpdate_giftCardUpdate_giftCard_user {
__typename: "User";
id: string;
firstName: string;
lastName: string;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_usedBy { export interface GiftCardUpdate_giftCardUpdate_giftCard_usedBy {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -60,12 +53,6 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard_app {
name: string | null; name: string | null;
} }
export interface GiftCardUpdate_giftCardUpdate_giftCard_expiryPeriod {
__typename: "TimePeriod";
amount: number;
type: TimePeriodTypeEnum;
}
export interface GiftCardUpdate_giftCardUpdate_giftCard_initialBalance { export interface GiftCardUpdate_giftCardUpdate_giftCard_initialBalance {
__typename: "Money"; __typename: "Money";
amount: number; amount: number;
@ -83,17 +70,15 @@ export interface GiftCardUpdate_giftCardUpdate_giftCard {
metadata: (GiftCardUpdate_giftCardUpdate_giftCard_metadata | null)[]; metadata: (GiftCardUpdate_giftCardUpdate_giftCard_metadata | null)[];
privateMetadata: (GiftCardUpdate_giftCardUpdate_giftCard_privateMetadata | null)[]; privateMetadata: (GiftCardUpdate_giftCardUpdate_giftCard_privateMetadata | null)[];
displayCode: string; displayCode: string;
boughtInChannel: string | null;
createdBy: GiftCardUpdate_giftCardUpdate_giftCard_createdBy | null; createdBy: GiftCardUpdate_giftCardUpdate_giftCard_createdBy | null;
product: GiftCardUpdate_giftCardUpdate_giftCard_product | null; product: GiftCardUpdate_giftCardUpdate_giftCard_product | null;
user: GiftCardUpdate_giftCardUpdate_giftCard_user | null;
usedBy: GiftCardUpdate_giftCardUpdate_giftCard_usedBy | null; usedBy: GiftCardUpdate_giftCardUpdate_giftCard_usedBy | null;
usedByEmail: string | null; usedByEmail: string | null;
createdByEmail: string | null; createdByEmail: string | null;
app: GiftCardUpdate_giftCardUpdate_giftCard_app | null; app: GiftCardUpdate_giftCardUpdate_giftCard_app | null;
created: any; created: any;
expiryDate: any | null; expiryDate: any | null;
expiryType: GiftCardExpiryTypeEnum;
expiryPeriod: GiftCardUpdate_giftCardUpdate_giftCard_expiryPeriod | null;
lastUsedOn: any | null; lastUsedOn: any | null;
isActive: boolean; isActive: boolean;
initialBalance: GiftCardUpdate_giftCardUpdate_giftCard_initialBalance | null; initialBalance: GiftCardUpdate_giftCardUpdate_giftCard_initialBalance | null;

View file

@ -0,0 +1,14 @@
import Container from "@saleor/components/Container";
import React from "react";
import GiftCardsListHeader from "./GiftCardsListHeader";
import GiftCardsListTable from "./GiftCardsListTable";
const GiftCardsListPage: React.FC = () => (
<Container>
<GiftCardsListHeader />
<GiftCardsListTable />
</Container>
);
export default GiftCardsListPage;

View file

@ -1,46 +1,20 @@
import Container from "@saleor/components/Container";
import useNavigator from "@saleor/hooks/useNavigator";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import React from "react"; import React from "react";
import GiftCardCreateDialog from "../GiftCardCreateDialog"; import GiftCardListPage from "./GiftCardListPage";
import { giftCardsListUrl } from "../urls"; import GiftCardListDialogsProvider from "./providers/GiftCardListDialogsProvider";
import GiftCardsListHeader from "./GiftCardsListHeader"; import { GiftCardsListProvider } from "./providers/GiftCardListProvider";
import GiftCardsListTable from "./GiftCardsListTable"; import { GiftCardListUrlQueryParams } from "./types";
import { GiftCardsListProvider } from "./providers/GiftCardsListProvider";
import {
GiftCardListActionParamsEnum,
GiftCardListUrlQueryParams
} from "./types";
interface GiftCardsListProps { interface GiftCardsListProps {
params: GiftCardListUrlQueryParams; params: GiftCardListUrlQueryParams;
} }
const GiftCardsList: React.FC<GiftCardsListProps> = ({ params }) => { const GiftCardsList: React.FC<GiftCardsListProps> = ({ params }) => (
const navigate = useNavigator();
const [openModal, closeModal] = createDialogActionHandlers<
GiftCardListActionParamsEnum,
GiftCardListUrlQueryParams
>(navigate, giftCardsListUrl, params);
const openCreateModal = () => openModal(GiftCardListActionParamsEnum.CREATE);
return (
<>
<GiftCardsListProvider params={params}> <GiftCardsListProvider params={params}>
<Container> <GiftCardListDialogsProvider params={params}>
<GiftCardsListHeader onIssueButtonClick={openCreateModal} /> <GiftCardListPage />
<GiftCardsListTable /> </GiftCardListDialogsProvider>
</Container>
</GiftCardsListProvider> </GiftCardsListProvider>
<GiftCardCreateDialog );
open={params?.action === GiftCardListActionParamsEnum.CREATE}
onClose={closeModal}
/>
</>
);
};
export default GiftCardsList; export default GiftCardsList;

View file

@ -1,54 +0,0 @@
import { Button } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
// import CardMenu, { CardMenuItem } from "@saleor/components/CardMenu";
import PageHeader from "@saleor/components/PageHeader";
import { sectionNames } from "@saleor/intl";
import React from "react";
import { useIntl } from "react-intl";
import { giftCardsListHeaderMenuItemsMessages as messages } from "./messages";
interface GiftCardsListHeaderProps {
onIssueButtonClick: () => void;
}
const GiftCardsListHeader: React.FC<GiftCardsListHeaderProps> = ({
onIssueButtonClick
}) => {
const intl = useIntl();
// const menuItems: CardMenuItem[] = [
// {
// label: intl.formatMessage(messages.settings),
// testId: "settingsMenuItem"
// // onSelect:
// },
// {
// label: intl.formatMessage(messages.bulkIssue),
// testId: "bulkIssueMenuItem"
// // onSelect:
// },
// {
// label: intl.formatMessage(messages.exportCodes),
// testId: "exportCodesMenuItem"
// // onSelect:
// }
// ];
return (
<PageHeader title={intl.formatMessage(sectionNames.giftCards)}>
{/* <CardMenu menuItems={menuItems} data-test="menu" /> */}
<HorizontalSpacer spacing={2} />
<Button
color="primary"
variant="contained"
onClick={onIssueButtonClick}
data-test-id="issueCardButton"
>
{intl.formatMessage(messages.issueButtonLabel)}
</Button>
</PageHeader>
);
};
export default GiftCardsListHeader;

View file

@ -0,0 +1,103 @@
import { Button } from "@material-ui/core";
import HorizontalSpacer from "@saleor/apps/components/HorizontalSpacer";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import CardMenu, { CardMenuItem } from "@saleor/components/CardMenu";
import PageHeader from "@saleor/components/PageHeader";
import useNavigator from "@saleor/hooks/useNavigator";
import { sectionNames } from "@saleor/intl";
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";
import { giftCardSettingsUrl } from "../../urls";
import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages";
import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
import { useGiftCardProductsCountQuery } from "../queries";
import GiftCardsListHeaderAlertContent from "./GiftCardsListHeaderAlertContent";
const GiftCardsListHeader: React.FC = () => {
const intl = useIntl();
const navigate = useNavigator();
const { openCreateDialog } = useGiftCardListDialogs();
const {
data: giftCardProductsCount,
loading: giftCardProductsCountLoading
} = useGiftCardProductsCountQuery();
const openSettings = () => navigate(giftCardSettingsUrl);
const menuItems: CardMenuItem[] = [
{
label: intl.formatMessage(messages.settings),
testId: "settingsMenuItem",
onSelect: openSettings
}
// {
// label: intl.formatMessage(messages.bulkIssue),
// testId: "bulkIssueMenuItem"
// // onSelect:
// },
// {
// label: intl.formatMessage(messages.exportCodes),
// testId: "exportCodesMenuItem"
// // onSelect:
// }
];
const giftCardProductTypesExist =
giftCardProductsCount?.giftCardProductTypes.totalCount > 0;
const giftCardProductsExist =
giftCardProductsCount?.giftCardProducts.totalCount > 0;
const showNoGiftCardProductsAlert =
!giftCardProductsCountLoading &&
(!giftCardProductTypesExist || !giftCardProductsExist);
const handleCreateGiftCardProductType = () =>
navigate(
productTypeAddUrl({
kind: ProductTypeKindEnum.GIFT_CARD
})
);
const handleCreateGiftCardProduct = () => navigate(productAddUrl());
return (
<>
<PageHeader title={intl.formatMessage(sectionNames.giftCards)}>
<CardMenu menuItems={menuItems} data-test="menu" />
<HorizontalSpacer spacing={2} />
<Button
color="primary"
variant="contained"
onClick={openCreateDialog}
data-test-id="issueCardButton"
>
{intl.formatMessage(messages.issueButtonLabel)}
</Button>
</PageHeader>
{showNoGiftCardProductsAlert && (
<Alert
title={intl.formatMessage(messages.noGiftCardsAlertTitle)}
variant="warning"
close={false}
>
<GiftCardsListHeaderAlertContent
giftCardProductTypesExist={giftCardProductTypesExist}
giftCardProductsExist={giftCardProductsExist}
handleCreateGiftCardProductType={handleCreateGiftCardProductType}
handleCreateGiftCardProduct={handleCreateGiftCardProduct}
/>
</Alert>
)}
<VerticalSpacer spacing={2} />
</>
);
};
export default GiftCardsListHeader;

View file

@ -0,0 +1,62 @@
import Link from "@saleor/components/Link";
import React from "react";
import { FormattedMessage } from "react-intl";
import { giftCardsListHeaderMenuItemsMessages as messages } from "../messages";
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
}) => {
const classes = useStyles({});
if (!giftCardProductTypesExist) {
return (
<FormattedMessage
{...messages.noGiftCardsProductTypes}
values={{
createGiftCardProductType: (
<Link
onClick={handleCreateGiftCardProductType}
className={classes.alertLink}
>
<FormattedMessage {...messages.createGiftCardProductType} />
</Link>
)
}}
/>
);
}
if (!giftCardProductsExist) {
return (
<FormattedMessage
{...messages.noGiftCardsProducts}
values={{
createGiftCardProduct: (
<Link
onClick={handleCreateGiftCardProduct}
className={classes.alertLink}
>
<FormattedMessage {...messages.createGiftCardProduct} />
</Link>
)
}}
/>
);
}
return null;
};
export default GiftCardsListHeaderAlertContent;

View file

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

View file

@ -22,8 +22,9 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { giftCardsListTableMessages as messages } from "../messages"; import { giftCardsListTableMessages as messages } from "../messages";
import useGiftCardList from "../providers/hooks/useGiftCardList"; import useGiftCardListDialogs from "../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
import useGiftCardListBulkActions from "../providers/hooks/useGiftCardListBulkActions"; import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
import useGiftCardListBulkActions from "../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { useTableStyles as useStyles } from "../styles"; import { useTableStyles as useStyles } from "../styles";
import GiftCardsListTableFooter from "./GiftCardsListTableFooter"; import GiftCardsListTableFooter from "./GiftCardsListTableFooter";
import GiftCardsListTableHeader from "./GiftCardsListTableHeader"; import GiftCardsListTableHeader from "./GiftCardsListTableHeader";
@ -34,8 +35,10 @@ const GiftCardsListTable: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
const navigate = useNavigator(); const navigate = useNavigator();
const { giftCards, numberOfColumns, loading } = useGiftCardList(); const { giftCards, numberOfColumns, loading } = useGiftCardList();
const { toggle, isSelected } = useGiftCardListBulkActions(); const { toggle, isSelected } = useGiftCardListBulkActions();
const { openDeleteDialog } = useGiftCardListDialogs();
const redirectToGiftCardUpdate = (id: string) => () => const redirectToGiftCardUpdate = (id: string) => () =>
navigate(giftCardUrl(id)); navigate(giftCardUrl(id));
@ -122,7 +125,12 @@ const GiftCardsListTable: React.FC = () => {
</div> </div>
</TableCell> </TableCell>
<TableCell className={classes.colDelete}> <TableCell className={classes.colDelete}>
<DeleteIconButton /> <DeleteIconButton
onClick={event => {
event.stopPropagation();
openDeleteDialog(id);
}}
/>
</TableCell> </TableCell>
</TableRow> </TableRow>
), ),

View file

@ -3,7 +3,7 @@ import TablePagination from "@saleor/components/TablePagination";
import usePaginator from "@saleor/hooks/usePaginator"; import usePaginator from "@saleor/hooks/usePaginator";
import React from "react"; import React from "react";
import useGiftCardList from "../providers/hooks/useGiftCardList"; import useGiftCardList from "../providers/GiftCardListProvider/hooks/useGiftCardList";
const GiftCardsListTableFooter: React.FC = () => { const GiftCardsListTableFooter: React.FC = () => {
const paginate = usePaginator(); const paginate = usePaginator();

View file

@ -0,0 +1,120 @@
import ConfirmButton from "@saleor/components/ConfirmButton";
import { IMessage } from "@saleor/components/messages";
import useNotifier from "@saleor/hooks/useNotifier";
import { getByIds } from "@saleor/orders/components/OrderReturnPage/utils";
import React from "react";
import { useIntl } from "react-intl";
import useGiftCardList from "../../providers/GiftCardListProvider/hooks/useGiftCardList";
import useGiftCardListBulkActions from "../../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { GIFT_CARD_LIST_QUERY } from "../../types";
import { bulkEnableDisableSectionMessages as messages } from "./messages";
import {
useGiftCardBulkActivateMutation,
useGiftCardBulkDeactivateMutation
} from "./mutations";
import { GiftCardBulkActivate } from "./types/GiftCardBulkActivate";
import { GiftCardBulkDeactivate } from "./types/GiftCardBulkDeactivate";
const BulkEnableDisableSection: React.FC = () => {
const intl = useIntl();
const notify = useNotifier();
const { listElements: ids, reset } = useGiftCardListBulkActions();
const { giftCards } = useGiftCardList();
const onActivateCompleted = (data: GiftCardBulkActivate) => {
const { errors, count } = data?.giftCardBulkActivate;
const notifierData: IMessage = !!errors?.length
? {
status: "error",
text: intl.formatMessage(messages.errorActivateAlertText, { count })
}
: {
status: "success",
text: intl.formatMessage(messages.successActivateAlertText, { count })
};
notify(notifierData);
if (!errors.length) {
reset();
}
};
const onDeactivateCompleted = (data: GiftCardBulkDeactivate) => {
const { errors, count } = data?.giftCardBulkDeactivate;
const notifierData: IMessage = !!errors?.length
? {
status: "error",
text: intl.formatMessage(messages.errorDeactivateAlertText, { count })
}
: {
status: "success",
text: intl.formatMessage(messages.successDeactivateAlertText, {
count
})
};
notify(notifierData);
if (!errors.length) {
reset();
}
};
const hasAnyEnabledCardsSelected = giftCards
.filter(getByIds(ids))
.some(({ isActive }) => isActive);
const hasAnyDisabledCardsSelected = giftCards
.filter(getByIds(ids))
.some(({ isActive }) => !isActive);
const [
activateGiftCards,
activateGiftCardsOpts
] = useGiftCardBulkActivateMutation({
onCompleted: onActivateCompleted,
refetchQueries: [GIFT_CARD_LIST_QUERY]
});
const [
deactivateGiftCards,
deactivateGiftCardsOpts
] = useGiftCardBulkDeactivateMutation({
onCompleted: onDeactivateCompleted,
refetchQueries: [GIFT_CARD_LIST_QUERY]
});
const handleActivateGiftCards = () =>
activateGiftCards({ variables: { ids } });
const handleDeactivateGiftCards = () =>
deactivateGiftCards({ variables: { ids } });
return (
<>
<ConfirmButton
disabled={hasAnyEnabledCardsSelected}
onClick={handleActivateGiftCards}
variant="text"
transitionState={activateGiftCardsOpts?.status}
>
{intl.formatMessage(messages.enableLabel)}
</ConfirmButton>
<ConfirmButton
disabled={hasAnyDisabledCardsSelected}
onClick={handleDeactivateGiftCards}
variant="text"
transitionState={deactivateGiftCardsOpts?.status}
>
{intl.formatMessage(messages.disableLabel)}
</ConfirmButton>
</>
);
};
export default BulkEnableDisableSection;

View file

@ -1,4 +1,5 @@
import { TableCell } from "@material-ui/core"; import { TableCell } from "@material-ui/core";
import DeleteIconButton from "@saleor/components/DeleteIconButton";
import TableCellHeader, { import TableCellHeader, {
TableCellHeaderProps TableCellHeaderProps
} from "@saleor/components/TableCellHeader"; } from "@saleor/components/TableCellHeader";
@ -9,10 +10,12 @@ import Label, {
import React from "react"; import React from "react";
import { MessageDescriptor, useIntl } from "react-intl"; import { MessageDescriptor, useIntl } from "react-intl";
import { giftCardsListTableMessages as messages } from "../messages"; import { giftCardsListTableMessages as messages } from "../../messages";
import useGiftCardList from "../providers/hooks/useGiftCardList"; import useGiftCardListDialogs from "../../providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs";
import useGiftCardListBulkActions from "../providers/hooks/useGiftCardListBulkActions"; import useGiftCardList from "../../providers/GiftCardListProvider/hooks/useGiftCardList";
import { useTableStyles as useStyles } from "../styles"; import useGiftCardListBulkActions from "../../providers/GiftCardListProvider/hooks/useGiftCardListBulkActions";
import { useTableStyles as useStyles } from "../../styles";
import BulkEnableDisableSection from "./BulkEnableDisableSection";
interface HeaderItem { interface HeaderItem {
title?: MessageDescriptor; title?: MessageDescriptor;
@ -22,8 +25,10 @@ interface HeaderItem {
const GiftCardsListTableHeader: React.FC = () => { const GiftCardsListTableHeader: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
const { giftCards, numberOfColumns, loading } = useGiftCardList(); const { giftCards, numberOfColumns, loading } = useGiftCardList();
const { toggleAll, listElements } = useGiftCardListBulkActions(); const { toggleAll, listElements } = useGiftCardListBulkActions();
const { openDeleteDialog } = useGiftCardListDialogs();
const headerItems: HeaderItem[] = [ const headerItems: HeaderItem[] = [
{ {
@ -68,6 +73,12 @@ const GiftCardsListTableHeader: React.FC = () => {
selected={listElements.length} selected={listElements.length}
items={giftCards} items={giftCards}
toggleAll={toggleAll} toggleAll={toggleAll}
toolbar={
<>
<BulkEnableDisableSection />
<DeleteIconButton onClick={openDeleteDialog} />
</>
}
> >
{headerItems.map(({ title, options }) => ( {headerItems.map(({ title, options }) => (
<TableCellHeader {...options}> <TableCellHeader {...options}>

View file

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

View file

@ -0,0 +1,32 @@
import { defineMessages } from "react-intl";
export const bulkEnableDisableSectionMessages = defineMessages({
enableLabel: {
defaultMessage: "Activate",
description: "GiftCardEnableDisableSection enable label"
},
disableLabel: {
defaultMessage: "Deactivate",
description: "GiftCardEnableDisableSection enable label"
},
successActivateAlertText: {
defaultMessage:
"Successfully activated gift {count,plural,one{card} other{cards}}",
description: "GiftCardEnableDisableSection success activate alert text"
},
successDeactivateAlertText: {
defaultMessage:
"Successfully deactivated gift {count,plural,one{card} other{cards}}",
description: "GiftCardEnableDisableSection success activate alert text"
},
errorActivateAlertText: {
defaultMessage:
"Error activating gift {count,plural,one{card} other{cards}}",
description: "GiftCardEnableDisableSection error activate alert text"
},
errorDeactivateAlertText: {
defaultMessage:
"Errors deactivating gift {count,plural,one{card} other{cards}}",
description: "GiftCardEnableDisableSection error activate alert text"
}
});

View file

@ -0,0 +1,46 @@
import { giftCardErrorFragment } from "@saleor/fragments/errors";
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import {
GiftCardBulkActivate,
GiftCardBulkActivateVariables
} from "./types/GiftCardBulkActivate";
import {
GiftCardBulkDeactivate,
GiftCardBulkDeactivateVariables
} from "./types/GiftCardBulkDeactivate";
const giftCardBulkActivate = gql`
${giftCardErrorFragment}
mutation GiftCardBulkActivate($ids: [ID]!) {
giftCardBulkActivate(ids: $ids) {
errors {
...GiftCardError
}
count
}
}
`;
export const useGiftCardBulkActivateMutation = makeMutation<
GiftCardBulkActivate,
GiftCardBulkActivateVariables
>(giftCardBulkActivate);
const giftCardBulkDeactivate = gql`
${giftCardErrorFragment}
mutation GiftCardBulkDeactivate($ids: [ID]!) {
giftCardBulkDeactivate(ids: $ids) {
errors {
...GiftCardError
}
count
}
}
`;
export const useGiftCardBulkDeactivateMutation = makeMutation<
GiftCardBulkDeactivate,
GiftCardBulkDeactivateVariables
>(giftCardBulkDeactivate);

View file

@ -0,0 +1,30 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardErrorCode } from "./../../../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: GiftCardBulkActivate
// ====================================================
export interface GiftCardBulkActivate_giftCardBulkActivate_errors {
__typename: "GiftCardError";
code: GiftCardErrorCode;
field: string | null;
}
export interface GiftCardBulkActivate_giftCardBulkActivate {
__typename: "GiftCardBulkActivate";
errors: GiftCardBulkActivate_giftCardBulkActivate_errors[];
count: number;
}
export interface GiftCardBulkActivate {
giftCardBulkActivate: GiftCardBulkActivate_giftCardBulkActivate | null;
}
export interface GiftCardBulkActivateVariables {
ids: (string | null)[];
}

View file

@ -0,0 +1,30 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardErrorCode } from "./../../../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: GiftCardBulkDeactivate
// ====================================================
export interface GiftCardBulkDeactivate_giftCardBulkDeactivate_errors {
__typename: "GiftCardError";
code: GiftCardErrorCode;
field: string | null;
}
export interface GiftCardBulkDeactivate_giftCardBulkDeactivate {
__typename: "GiftCardBulkDeactivate";
errors: GiftCardBulkDeactivate_giftCardBulkDeactivate_errors[];
count: number;
}
export interface GiftCardBulkDeactivate {
giftCardBulkDeactivate: GiftCardBulkDeactivate_giftCardBulkDeactivate | null;
}
export interface GiftCardBulkDeactivateVariables {
ids: (string | null)[];
}

View file

@ -16,6 +16,37 @@ export const giftCardsListHeaderMenuItemsMessages = defineMessages({
issueButtonLabel: { issueButtonLabel: {
defaultMessage: "Issue card", defaultMessage: "Issue card",
description: "GiftCardsListHeader issue button label" description: "GiftCardsListHeader issue button label"
},
noGiftCardsAlertTitle: {
defaultMessage: "You havent defined a gift card product!",
description: "GiftCardsListHeader alert"
},
noGiftCardsProductsAndProductTypes: {
defaultMessage:
"{createGiftCardProductType} and {giftCardProduct} to start selling gift cards in your store.",
description: "GiftCardsListHeader alert"
},
noGiftCardsProductTypes: {
defaultMessage:
"{createGiftCardProductType} to start selling gift cards in your store.",
description: "GiftCardsListHeader alert"
},
noGiftCardsProducts: {
defaultMessage:
"{createGiftCardProduct} to start selling gift cards in your store.",
description: "GiftCardsListHeader alert"
},
createGiftCardProductType: {
defaultMessage: "Create a gift card product type",
description: "GiftCardsListHeader alert"
},
createGiftCardProduct: {
defaultMessage: "Create a gift card product",
description: "GiftCardsListHeader alert"
},
giftCardProduct: {
defaultMessage: "gift card product",
description: "GiftCardsListHeader alert"
} }
}); });

View file

@ -0,0 +1,44 @@
import { giftCardErrorFragment } from "@saleor/fragments/errors";
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import {
BulkDeleteGiftCard,
BulkDeleteGiftCardVariables
} from "./types/BulkDeleteGiftCard";
import {
DeleteGiftCard,
DeleteGiftCardVariables
} from "./types/DeleteGiftCard";
const deleteGiftCard = gql`
${giftCardErrorFragment}
mutation DeleteGiftCard($id: ID!) {
giftCardDelete(id: $id) {
errors {
...GiftCardError
}
}
}
`;
export const useGiftCardDeleteMutation = makeMutation<
DeleteGiftCard,
DeleteGiftCardVariables
>(deleteGiftCard);
const bulkDeleteGiftCard = gql`
${giftCardErrorFragment}
mutation BulkDeleteGiftCard($ids: [ID]!) {
giftCardBulkDelete(ids: $ids) {
errors {
...GiftCardError
}
}
}
`;
export const useGiftCardBulkDeleteMutation = makeMutation<
BulkDeleteGiftCard,
BulkDeleteGiftCardVariables
>(bulkDeleteGiftCard);

View file

@ -0,0 +1,80 @@
import GiftCardListPageDeleteDialog from "@saleor/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog";
import GiftCardCreateDialog from "@saleor/giftCards/GiftCardCreateDialog";
import { giftCardsListUrl } from "@saleor/giftCards/urls";
import useNavigator from "@saleor/hooks/useNavigator";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import React, { createContext } from "react";
import {
GiftCardListActionParamsEnum,
GiftCardListUrlQueryParams
} from "../../types";
interface GiftCardListDialogsProviderProps {
children: React.ReactNode;
params: GiftCardListUrlQueryParams;
}
export interface GiftCardListDialogsConsumerProps {
openCreateDialog: () => void;
openDeleteDialog: (id?: string | React.MouseEvent) => void;
closeDialog: () => void;
id: string;
}
export const GiftCardListDialogsContext = createContext<
GiftCardListDialogsConsumerProps
>(null);
const GiftCardListDialogsProvider: React.FC<GiftCardListDialogsProviderProps> = ({
children,
params
}) => {
const navigate = useNavigator();
const id = params?.id;
const [openDialog, closeDialog] = createDialogActionHandlers<
GiftCardListActionParamsEnum,
GiftCardListUrlQueryParams
>(navigate, giftCardsListUrl, params);
const openCreateDialog = () =>
openDialog(GiftCardListActionParamsEnum.CREATE);
const isCreateDialogOpen =
params?.action === GiftCardListActionParamsEnum.CREATE;
const isDeleteDialogOpen =
params?.action === GiftCardListActionParamsEnum.DELETE;
const handleDeleteDialogOpen = (id?: string) => {
openDialog(
GiftCardListActionParamsEnum.DELETE,
typeof id === "string" ? { id } : undefined
);
};
const providerValues: GiftCardListDialogsConsumerProps = {
openCreateDialog,
openDeleteDialog: handleDeleteDialogOpen,
closeDialog,
id
};
return (
<GiftCardListDialogsContext.Provider value={providerValues}>
{children}
<GiftCardCreateDialog
open={isCreateDialogOpen}
closeDialog={closeDialog}
/>
<GiftCardListPageDeleteDialog
open={isDeleteDialogOpen}
closeDialog={closeDialog}
/>
</GiftCardListDialogsContext.Provider>
);
};
export default GiftCardListDialogsProvider;

View file

@ -0,0 +1,14 @@
import { useContext } from "react";
import {
GiftCardListDialogsConsumerProps,
GiftCardListDialogsContext
} from "../GiftCardListDialogsProvider";
const useGiftCardListDialogs = (): GiftCardListDialogsConsumerProps => {
const giftCardListDialogsProps = useContext(GiftCardListDialogsContext);
return giftCardListDialogsProps;
};
export default useGiftCardListDialogs;

View file

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

View file

@ -0,0 +1,98 @@
import useBulkActions, {
UseBulkActionsProps
} from "@saleor/hooks/useBulkActions";
import useListSettings, {
UseListSettings
} from "@saleor/hooks/useListSettings";
import {
createPaginationState,
PageInfo,
PaginationState
} from "@saleor/hooks/usePaginator";
import { ListViews } from "@saleor/types";
import { mapEdgesToItems } from "@saleor/utils/maps";
import React, { createContext } from "react";
import { useGiftCardListQuery } from "../../queries";
import { GiftCardListColummns, GiftCardListUrlQueryParams } from "../../types";
import {
GiftCardList_giftCards_edges_node,
GiftCardListVariables
} from "../../types/GiftCardList";
const numberOfColumns = 7;
interface GiftCardsListProviderProps {
children: React.ReactNode;
params: GiftCardListUrlQueryParams;
}
export interface GiftCardListDataProps {
giftCards: GiftCardList_giftCards_edges_node[];
pageInfo: PageInfo;
loading: boolean;
params: GiftCardListUrlQueryParams;
paginationState: PaginationState;
numberOfColumns: number;
}
export interface GiftCardsListConsumerProps
extends UseBulkActionsProps,
GiftCardListDataProps,
UseListSettings<GiftCardListColummns> {
selectedItemsCount: number;
}
export const GiftCardsListContext = createContext<GiftCardsListConsumerProps>(
null
);
export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
children,
params
}) => {
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
[]
);
const { updateListSettings, settings } = useListSettings<
GiftCardListColummns
>(ListViews.GIFT_CARD_LIST);
const paginationState = createPaginationState(settings.rowNumber, params);
const queryVariables = React.useMemo<GiftCardListVariables>(
() => ({
...paginationState
}),
[params]
);
const { data, loading } = useGiftCardListQuery({
displayLoader: true,
variables: queryVariables
});
const providerValues: GiftCardsListConsumerProps = {
giftCards: mapEdgesToItems(data?.giftCards) || [],
loading,
isSelected,
listElements,
reset,
toggleAll,
toggle,
selectedItemsCount: listElements.length,
pageInfo: data?.giftCards?.pageInfo,
paginationState,
params,
settings,
updateListSettings,
numberOfColumns
};
return (
<GiftCardsListContext.Provider value={providerValues}>
{children}
</GiftCardsListContext.Provider>
);
};

View file

@ -0,0 +1,37 @@
import { UseListSettings } from "@saleor/hooks/useListSettings";
import { useContext } from "react";
import { GiftCardListColummns } from "../../../types";
import {
GiftCardListDataProps,
GiftCardsListContext
} from "../GiftCardListProvider";
export type UseGiftCardListProps = GiftCardListDataProps &
UseListSettings<GiftCardListColummns>;
const useGiftCardList = (): UseGiftCardListProps => {
const {
giftCards,
loading,
pageInfo,
paginationState,
params,
settings,
updateListSettings,
numberOfColumns
} = useContext(GiftCardsListContext);
return {
giftCards,
loading,
pageInfo,
paginationState,
params,
settings,
updateListSettings,
numberOfColumns
};
};
export default useGiftCardList;

View file

@ -0,0 +1,32 @@
import { UseBulkActionsProps } from "@saleor/hooks/useBulkActions";
import { useContext } from "react";
import {
GiftCardsListConsumerProps,
GiftCardsListContext
} from "../GiftCardListProvider";
export type UseGiftCardListBulkActionsProps = UseBulkActionsProps &
Pick<GiftCardsListConsumerProps, "selectedItemsCount">;
const useGiftCardListBulkActions = (): UseGiftCardListBulkActionsProps => {
const {
isSelected,
listElements,
reset,
toggleAll,
toggle,
selectedItemsCount
} = useContext(GiftCardsListContext);
return {
isSelected,
listElements,
reset,
toggleAll,
toggle,
selectedItemsCount
};
};
export default useGiftCardListBulkActions;

View file

@ -0,0 +1 @@
export * from "./GiftCardListProvider";

View file

@ -59,7 +59,7 @@ export const GiftCardsListProvider: React.FC<GiftCardsListProviderProps> = ({
GiftCardListColummns GiftCardListColummns
>(ListViews.GIFT_CARD_LIST); >(ListViews.GIFT_CARD_LIST);
const paginationState = createPaginationState(settings.rowNumber, params); const paginationState = createPaginationState(settings?.rowNumber, params);
const queryVariables = React.useMemo<GiftCardListVariables>( const queryVariables = React.useMemo<GiftCardListVariables>(
() => ({ () => ({

View file

@ -5,6 +5,7 @@ import gql from "graphql-tag";
import { giftCardDataFragment } from "../GiftCardUpdate/queries"; import { giftCardDataFragment } from "../GiftCardUpdate/queries";
import { GiftCardList, GiftCardListVariables } from "./types/GiftCardList"; import { GiftCardList, GiftCardListVariables } from "./types/GiftCardList";
import { GiftCardProductsCount } from "./types/GiftCardProductsCount";
export const giftCardList = gql` export const giftCardList = gql`
${fragmentUserBase} ${fragmentUserBase}
@ -26,8 +27,22 @@ export const giftCardList = gql`
} }
} }
`; `;
export const useGiftCardListQuery = makeQuery< export const useGiftCardListQuery = makeQuery<
GiftCardList, GiftCardList,
GiftCardListVariables GiftCardListVariables
>(giftCardList); >(giftCardList);
export const giftCardProductsCount = gql`
query GiftCardProductsCount {
giftCardProductTypes: productTypes(filter: { kind: GIFT_CARD }) {
totalCount
}
giftCardProducts: products(filter: { giftCard: true }) {
totalCount
}
}
`;
export const useGiftCardProductsCountQuery = makeQuery<
GiftCardProductsCount,
never
>(giftCardProductsCount);

View file

@ -34,3 +34,12 @@ export const useTableStyles = makeStyles(
}), }),
{ name: "GiftCardsListTable" } { name: "GiftCardsListTable" }
); );
export const useHeaderStyles = makeStyles(
theme => ({
alertLink: {
fontSize: theme.typography.body2.fontSize
}
}),
{ name: "GiftCardsListHeader" }
);

View file

@ -1,4 +1,4 @@
import { Dialog, Pagination } from "@saleor/types"; import { Dialog, Pagination, SingleAction } from "@saleor/types";
export type GiftCardListColummns = export type GiftCardListColummns =
| "giftCardCode" | "giftCardCode"
@ -8,8 +8,12 @@ export type GiftCardListColummns =
| "product"; | "product";
export enum GiftCardListActionParamsEnum { export enum GiftCardListActionParamsEnum {
CREATE = "gift-card-create" CREATE = "gift-card-create",
DELETE = "gift-card-delete"
} }
export type GiftCardListUrlQueryParams = Pagination & export type GiftCardListUrlQueryParams = Pagination &
Dialog<GiftCardListActionParamsEnum>; Dialog<GiftCardListActionParamsEnum> &
SingleAction;
export const GIFT_CARD_LIST_QUERY = "GiftCardList";

View file

@ -0,0 +1,29 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { GiftCardErrorCode } from "./../../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: BulkDeleteGiftCard
// ====================================================
export interface BulkDeleteGiftCard_giftCardBulkDelete_errors {
__typename: "GiftCardError";
code: GiftCardErrorCode;
field: string | null;
}
export interface BulkDeleteGiftCard_giftCardBulkDelete {
__typename: "GiftCardBulkDelete";
errors: BulkDeleteGiftCard_giftCardBulkDelete_errors[];
}
export interface BulkDeleteGiftCard {
giftCardBulkDelete: BulkDeleteGiftCard_giftCardBulkDelete | null;
}
export interface BulkDeleteGiftCardVariables {
ids: (string | null)[];
}

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