From aaa0a9f309206fe91b759cccf937a9cf6035b4b8 Mon Sep 17 00:00:00 2001 From: Magdalena Markusik Date: Tue, 14 Sep 2021 15:57:02 +0200 Subject: [PATCH] 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 * Refactor * Refactor * Set common error codes in string union Co-authored-by: Magdalena Markusik * 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 --- locale/defaultMessages.json | 242 ++- schema.graphql | 150 +- src/components/ActionDialog/ActionDialog.tsx | 2 +- .../AppLayout/AppChannelContext.tsx | 3 +- .../ChannelAvailabilityItemContent.tsx | 4 +- .../ExtendedPageHeader/ExtendedPageHeader.tsx | 33 +- src/components/PageHeader/PageHeader.tsx | 4 +- .../RadioGroupField/RadioGroupField.tsx | 41 +- src/components/RadioGroupField/styles.ts | 43 + .../DeleteWarningDialogConsentContent.tsx | 46 + .../TypeDeleteWarningDialogContent.tsx | 33 +- .../VisibilityCard/VisibilityCard.tsx | 4 +- src/fragments/errors.ts | 7 + src/fragments/giftCards.ts | 11 + src/fragments/orders.ts | 22 + src/fragments/productTypes.ts | 1 + .../types/GiftCardSettingsErrorFragment.ts | 16 + .../types/GiftCardsSettingsFragment.ts | 22 + src/fragments/types/OrderDetailsFragment.ts | 48 +- src/fragments/types/OrderSettingsFragment.ts | 1 + .../types/ProductTypeDetailsFragment.ts | 3 +- src/fragments/types/ProductTypeFragment.ts | 3 + .../GiftCardCreateDialog.tsx | 32 +- .../GiftCardCreateDialogForm.tsx | 136 +- .../GiftCardCreateExpirySelect.tsx | 137 ++ .../GiftCardCreateExpirySelect/index.tsx | 2 + .../GiftCardCreateExpirySelect/messages.ts | 20 + .../GiftCardCreateExpirySelect/styles.ts | 20 + .../GiftCardCreateDialog/messages.ts | 13 +- src/giftCards/GiftCardCreateDialog/types.ts | 22 +- src/giftCards/GiftCardCreateDialog/utils.ts | 85 +- .../GiftCardExpirySettingsCard.tsx | 45 + .../GiftCardExpirySettingsCard/index.ts | 2 + .../GiftCardExpirySettingsCard/messages.ts | 12 + .../GiftCardSettings/GiftCardSettingsPage.tsx | 101 ++ src/giftCards/GiftCardSettings/index.tsx | 2 + src/giftCards/GiftCardSettings/messages.ts | 17 + src/giftCards/GiftCardSettings/mutations.ts | 29 + src/giftCards/GiftCardSettings/queries.ts | 18 + src/giftCards/GiftCardSettings/types.ts | 7 + .../types/GiftCardSettings.ts | 26 + .../types/GiftCardSettingsUpdate.ts | 42 + src/giftCards/GiftCardSettings/utils.ts | 32 + .../GiftCardResendCodeDialog.tsx | 179 +++ .../GiftCardResendCodeDialog/index.tsx | 2 + .../GiftCardResendCodeDialog/messages.ts | 33 + .../GiftCardResendCodeDialog/mutations.ts | 29 + .../types/GiftCardResend.ts | 102 ++ .../GiftCardResendCodeDialog/utils.ts | 34 + .../GiftCardUpdateBalanceDialog.tsx | 105 +- .../GiftCardUpdateDetailsCard.tsx | 13 +- .../GiftCardUpdateExpirySelect.tsx | 69 + .../GiftCardUpdateExpirySelect/index.tsx | 2 + .../GiftCardUpdateExpirySelect/messages.ts | 12 + .../GiftCardUpdateExpirySelect}/styles.ts | 8 +- .../GiftCardUpdateInfoCardContent.tsx | 40 +- .../GiftCardUpdate/GiftCardUpdatePage.tsx | 3 +- .../GiftCardEnableDisableSection.tsx | 20 +- .../GiftCardUpdatePageHeader.tsx | 13 + .../GiftCardUpdatePageHeader/messages.ts | 15 +- .../GiftCardUpdatePageHeader/styles.ts | 27 - .../types/GiftCardActivate.ts | 19 +- .../types/GiftCardDeactivate.ts | 19 +- src/giftCards/GiftCardUpdate/messages.ts | 28 +- .../GiftCardUpdateDialogsProvider.tsx | 42 +- .../GiftCardUpdateFormProvider.tsx | 24 +- src/giftCards/GiftCardUpdate/queries.ts | 23 +- src/giftCards/GiftCardUpdate/types.ts | 4 +- .../GiftCardUpdate/types/GiftCardData.ts | 19 +- .../GiftCardUpdate/types/GiftCardDetails.ts | 46 +- .../GiftCardUpdate/types/GiftCardUpdate.ts | 19 +- .../GiftCardsList/GiftCardListPage.tsx | 14 + src/giftCards/GiftCardsList/GiftCardsList.tsx | 48 +- .../GiftCardsList/GiftCardsListHeader.tsx | 54 - .../GiftCardsListHeader.tsx | 103 ++ .../GiftCardsListHeaderAlertContent.tsx | 62 + .../GiftCardsListHeader/index.tsx | 2 + .../GiftCardsListTable/GiftCardsListTable.tsx | 14 +- .../GiftCardsListTableFooter.tsx | 2 +- .../BulkEnableDisableSection.tsx | 120 ++ .../GiftCardsListTableHeader.tsx | 19 +- .../GiftCardsListTableHeader/index.tsx | 2 + .../GiftCardsListTableHeader/messages.ts | 32 + .../GiftCardsListTableHeader/mutations.ts | 46 + .../types/GiftCardBulkActivate.ts | 30 + .../types/GiftCardBulkDeactivate.ts | 30 + src/giftCards/GiftCardsList/messages.ts | 31 + src/giftCards/GiftCardsList/mutations.ts | 44 + .../GiftCardListDialogsProvider.tsx | 80 + .../hooks/useGiftCardListDialogs.ts | 14 + .../GiftCardListDialogsProvider/index.tsx | 2 + .../GiftCardListProvider.tsx | 98 ++ .../hooks/useGiftCardList.ts | 37 + .../hooks/useGiftCardListBulkActions.ts | 32 + .../providers/GiftCardListProvider/index.tsx | 1 + .../providers/GiftCardsListProvider.tsx | 2 +- src/giftCards/GiftCardsList/queries.ts | 17 +- src/giftCards/GiftCardsList/styles.ts | 9 + src/giftCards/GiftCardsList/types.ts | 10 +- .../GiftCardsList/types/BulkDeleteGiftCard.ts | 29 + .../GiftCardsList/types/DeleteGiftCard.ts | 29 + .../GiftCardsList/types/GiftCardList.ts | 19 +- .../types/GiftCardProductsCount.ts | 23 + .../GiftCardDeleteDialogContent.tsx | 123 ++ .../GiftCardListPageDeleteDialog.tsx | 69 + .../GiftCardUpdatePageDeleteDialog.tsx | 39 + .../GiftCardDeleteDialog/messages.ts | 29 + .../components/GiftCardDeleteDialog/styles.ts | 12 + .../useGiftCardBulkDelete.tsx | 71 + .../useGiftCardSingleDelete.tsx | 73 + .../GiftCardExpirySelect.tsx | 113 -- .../components/GiftCardExpirySelect/index.tsx | 2 - .../GiftCardExpirySelect/messages.ts | 20 - .../GiftCardSendToCustomer.tsx | 77 + .../GiftCardSendToCustomer/messages.ts | 22 + .../GiftCardSettingsExpirySelect.tsx | 72 + .../GiftCardSettingsExpirySelect/index.tsx | 2 + .../GiftCardSettingsExpirySelect/messages.ts | 13 + .../TimePeriodField/TimePeriodField.tsx | 4 + .../components/TimePeriodField/messages.ts | 10 +- src/giftCards/index.tsx | 4 +- src/giftCards/urls.ts | 2 + src/hooks/makeMutation.ts | 5 +- src/hooks/makeQuery.ts | 4 +- src/hooks/useCurrentDate.ts | 10 + .../components/OrderPayment/OrderPayment.tsx | 21 + src/orders/components/OrderPayment/utils.ts | 34 + .../components/OrderReturnPage/utils.tsx | 6 + .../OrderSettings/OrderSettings.tsx | 27 +- .../OrderSettingsPage/OrderSettingsPage.tsx | 1 + .../components/OrderSettingsPage/form.tsx | 3 + src/orders/fixtures.ts | 5 +- src/orders/types/FulfillOrder.ts | 48 +- src/orders/types/OrderCancel.ts | 48 +- src/orders/types/OrderCapture.ts | 48 +- src/orders/types/OrderConfirm.ts | 48 +- src/orders/types/OrderDetails.ts | 48 +- src/orders/types/OrderDiscountAdd.ts | 48 +- src/orders/types/OrderDiscountDelete.ts | 48 +- src/orders/types/OrderDiscountUpdate.ts | 48 +- src/orders/types/OrderDraftCancel.ts | 48 +- src/orders/types/OrderDraftFinalize.ts | 48 +- src/orders/types/OrderDraftUpdate.ts | 48 +- src/orders/types/OrderFulfillmentApprove.ts | 48 +- src/orders/types/OrderFulfillmentCancel.ts | 48 +- .../types/OrderFulfillmentRefundProducts.ts | 48 +- .../types/OrderFulfillmentUpdateTracking.ts | 48 +- src/orders/types/OrderLineDelete.ts | 48 +- src/orders/types/OrderLineDiscountRemove.ts | 48 +- src/orders/types/OrderLineDiscountUpdate.ts | 48 +- src/orders/types/OrderLineUpdate.ts | 48 +- src/orders/types/OrderLinesAdd.ts | 48 +- src/orders/types/OrderMarkAsPaid.ts | 48 +- src/orders/types/OrderRefund.ts | 48 +- src/orders/types/OrderSettings.ts | 1 + src/orders/types/OrderSettingsUpdate.ts | 1 + src/orders/types/OrderShippingMethodUpdate.ts | 48 +- src/orders/types/OrderUpdate.ts | 48 +- src/orders/types/OrderVoid.ts | 48 +- src/orders/views/OrderSettings.tsx | 2 +- .../ProductTypeCreatePage.tsx | 25 +- .../ProductTypeDetails/ProductTypeDetails.tsx | 68 +- .../components/ProductTypeDetails/messages.ts | 26 + .../ProductTypeDetailsPage.tsx | 4 + src/productTypes/fixtures.ts | 7 + src/productTypes/handlers.ts | 11 + src/productTypes/index.tsx | 15 +- .../types/AssignProductAttribute.ts | 3 +- .../types/ProductTypeAttributeReorder.ts | 3 +- src/productTypes/types/ProductTypeCreate.ts | 3 +- src/productTypes/types/ProductTypeDetails.ts | 3 +- src/productTypes/types/ProductTypeList.ts | 3 +- src/productTypes/types/ProductTypeUpdate.ts | 3 +- .../types/UnassignProductAttribute.ts | 3 +- src/productTypes/urls.ts | 8 +- src/productTypes/views/ProductTypeCreate.tsx | 28 +- .../views/ProductTypeList/ProductTypeList.tsx | 2 +- .../views/ProductTypeUpdate/index.tsx | 1 + .../views/ProductList/ProductList.tsx | 2 +- .../SiteSettingsPage/SiteSettingsPage.tsx | 1 + .../__snapshots__/Stories.test.ts.snap | 1341 +++++++++++++++-- .../productTypes/ProductTypeCreatePage.tsx | 9 +- src/types/globalTypes.ts | 42 +- src/utils/errors/account.ts | 13 +- src/utils/errors/app.ts | 13 +- src/utils/errors/attribute.ts | 13 +- src/utils/errors/channels.ts | 13 +- src/utils/errors/common.ts | 31 +- src/utils/errors/discounts.ts | 11 +- src/utils/errors/export.ts | 15 +- src/utils/errors/index.ts | 7 +- src/utils/errors/invoice.ts | 9 +- src/utils/errors/menu.ts | 19 +- src/utils/errors/order.ts | 13 +- src/utils/errors/page.ts | 13 +- src/utils/errors/permissionGroups.ts | 9 +- src/utils/errors/plugins.ts | 13 +- src/utils/errors/product.ts | 11 +- src/utils/errors/shipping.ts | 13 +- src/utils/errors/shop.ts | 13 +- src/utils/errors/stock.ts | 13 +- src/utils/errors/warehouse.ts | 18 +- src/utils/errors/webhooks.ts | 19 +- src/utils/handlers/dialogActionHandlers.ts | 4 +- .../WebhookEvents/WebhookEvents.tsx | 12 + 205 files changed, 6354 insertions(+), 1172 deletions(-) create mode 100644 src/components/RadioGroupField/styles.ts create mode 100644 src/components/TypeDeleteWarningDialog/DeleteWarningDialogConsentContent.tsx create mode 100644 src/fragments/giftCards.ts create mode 100644 src/fragments/types/GiftCardSettingsErrorFragment.ts create mode 100644 src/fragments/types/GiftCardsSettingsFragment.ts create mode 100644 src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/GiftCardCreateExpirySelect.tsx create mode 100644 src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/index.tsx create mode 100644 src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/messages.ts create mode 100644 src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/styles.ts create mode 100644 src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/GiftCardExpirySettingsCard.tsx create mode 100644 src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/index.ts create mode 100644 src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/messages.ts create mode 100644 src/giftCards/GiftCardSettings/GiftCardSettingsPage.tsx create mode 100644 src/giftCards/GiftCardSettings/index.tsx create mode 100644 src/giftCards/GiftCardSettings/messages.ts create mode 100644 src/giftCards/GiftCardSettings/mutations.ts create mode 100644 src/giftCards/GiftCardSettings/queries.ts create mode 100644 src/giftCards/GiftCardSettings/types.ts create mode 100644 src/giftCards/GiftCardSettings/types/GiftCardSettings.ts create mode 100644 src/giftCards/GiftCardSettings/types/GiftCardSettingsUpdate.ts create mode 100644 src/giftCards/GiftCardSettings/utils.ts create mode 100644 src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/GiftCardResendCodeDialog.tsx create mode 100644 src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/index.tsx create mode 100644 src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/messages.ts create mode 100644 src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/mutations.ts create mode 100644 src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/types/GiftCardResend.ts create mode 100644 src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/utils.ts create mode 100644 src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/GiftCardUpdateExpirySelect.tsx create mode 100644 src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/index.tsx create mode 100644 src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/messages.ts rename src/giftCards/{components/GiftCardExpirySelect => GiftCardUpdate/GiftCardUpdateExpirySelect}/styles.ts (70%) delete mode 100644 src/giftCards/GiftCardUpdate/GiftCardUpdatePageHeader/styles.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardListPage.tsx delete mode 100644 src/giftCards/GiftCardsList/GiftCardsListHeader.tsx create mode 100644 src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeader.tsx create mode 100644 src/giftCards/GiftCardsList/GiftCardsListHeader/GiftCardsListHeaderAlertContent.tsx create mode 100644 src/giftCards/GiftCardsList/GiftCardsListHeader/index.tsx create mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/BulkEnableDisableSection.tsx rename src/giftCards/GiftCardsList/GiftCardsListTable/{ => GiftCardsListTableHeader}/GiftCardsListTableHeader.tsx (72%) create mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/index.tsx create mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/messages.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/mutations.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/types/GiftCardBulkActivate.ts create mode 100644 src/giftCards/GiftCardsList/GiftCardsListTable/GiftCardsListTableHeader/types/GiftCardBulkDeactivate.ts create mode 100644 src/giftCards/GiftCardsList/mutations.ts create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/GiftCardListDialogsProvider.tsx create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/hooks/useGiftCardListDialogs.ts create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListDialogsProvider/index.tsx create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListProvider/GiftCardListProvider.tsx create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListProvider/hooks/useGiftCardList.ts create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListProvider/hooks/useGiftCardListBulkActions.ts create mode 100644 src/giftCards/GiftCardsList/providers/GiftCardListProvider/index.tsx create mode 100644 src/giftCards/GiftCardsList/types/BulkDeleteGiftCard.ts create mode 100644 src/giftCards/GiftCardsList/types/DeleteGiftCard.ts create mode 100644 src/giftCards/GiftCardsList/types/GiftCardProductsCount.ts create mode 100644 src/giftCards/components/GiftCardDeleteDialog/GiftCardDeleteDialogContent.tsx create mode 100644 src/giftCards/components/GiftCardDeleteDialog/GiftCardListPageDeleteDialog.tsx create mode 100644 src/giftCards/components/GiftCardDeleteDialog/GiftCardUpdatePageDeleteDialog.tsx create mode 100644 src/giftCards/components/GiftCardDeleteDialog/messages.ts create mode 100644 src/giftCards/components/GiftCardDeleteDialog/styles.ts create mode 100644 src/giftCards/components/GiftCardDeleteDialog/useGiftCardBulkDelete.tsx create mode 100644 src/giftCards/components/GiftCardDeleteDialog/useGiftCardSingleDelete.tsx delete mode 100644 src/giftCards/components/GiftCardExpirySelect/GiftCardExpirySelect.tsx delete mode 100644 src/giftCards/components/GiftCardExpirySelect/index.tsx delete mode 100644 src/giftCards/components/GiftCardExpirySelect/messages.ts create mode 100644 src/giftCards/components/GiftCardSendToCustomer/GiftCardSendToCustomer.tsx create mode 100644 src/giftCards/components/GiftCardSendToCustomer/messages.ts create mode 100644 src/giftCards/components/GiftCardSettingsExpirySelect/GiftCardSettingsExpirySelect.tsx create mode 100644 src/giftCards/components/GiftCardSettingsExpirySelect/index.tsx create mode 100644 src/giftCards/components/GiftCardSettingsExpirySelect/messages.ts create mode 100644 src/hooks/useCurrentDate.ts create mode 100644 src/orders/components/OrderPayment/utils.ts create mode 100644 src/productTypes/components/ProductTypeDetails/messages.ts create mode 100644 src/productTypes/handlers.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 4433ea235..a34688e8e 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3407,6 +3407,22 @@ "context": "gift cards section name", "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": { "context": "GiftCardCreateDialog amount label", "string": "Enter amount" @@ -3431,10 +3447,6 @@ "context": "GiftCardCreateDialog customer label", "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": { "context": "GiftCardCreateDialog issue button label", "string": "Issue" @@ -3447,10 +3459,57 @@ "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" }, + "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": { "context": "GiftCardCreateDialog title", "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": { "context": "GiftCardUpdateDetailsCard set balance dialog change button label", "string": "Change" @@ -3479,6 +3538,14 @@ "context": "GiftCardUpdateDetailsCard title", "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": { "context": "GiftCardUpdateInfoCard bought by label", "string": "Bought by" @@ -3511,13 +3578,9 @@ "context": "GiftCardUpdateInfoCard used by label", "string": "Used by" }, - "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_disableLabel": { - "context": "GiftCardEnableDisableSection enable label", - "string": "Disable" - }, - "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_enableLabel": { - "context": "GiftCardEnableDisableSection enable label", - "string": "Enable" + "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_resendButtonLabel": { + "context": "giftCardUpdatePageHeader resendButtonLabel", + "string": "Resend code" }, "src_dot_giftCards_dot_GiftCardUpdate_dot_GiftCardUpdatePageHeader_dot_successfullyDisabledTitle": { "context": "GiftCardEnableDisableSection disable success", @@ -3527,10 +3590,38 @@ "context": "GiftCardEnableDisableSection enable success", "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": { "context": "GiftCardUpdateDetailsCard title", "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": { "context": "GiftCardsListHeader menu item settings", "string": "Bulk Issue" @@ -3539,6 +3630,14 @@ "context": "GiftCardsListTable code ending with label", "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": { "context": "GiftCardsListHeader menu item settings", "string": "Export card codes" @@ -3547,6 +3646,10 @@ "context": "GiftCardsListTable disabled label", "string": "Disabled" }, + "src_dot_giftCards_dot_GiftCardsList_dot_giftCardProduct": { + "context": "GiftCardsListHeader alert", + "string": "gift card product" + }, "src_dot_giftCards_dot_GiftCardsList_dot_giftCardsTableColumnBalanceTitle": { "context": "GiftCardsListTable column title balance", "string": "Balance" @@ -3571,29 +3674,73 @@ "context": "GiftCardsListHeader issue button label", "string": "Issue card" }, + "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsAlertTitle": { + "context": "GiftCardsListHeader alert", + "string": "You haven’t defined a gift card product!" + }, "src_dot_giftCards_dot_GiftCardsList_dot_noGiftCardsFound": { "context": "GiftCardsListTable no cards found title", "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": { "context": "GiftCardsListHeader menu item settings", "string": "Settings" }, - "src_dot_giftCards_dot_components_dot_GiftCardExpirySelect_dot_expirationDateLabel": { - "context": "GiftCarUpdateDetailsExpirySection expiration date label", - "string": "Expiration date" + "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_consentLabel": { + "context": "GiftCardDeleteDialog consent label", + "string": "{selectedItemsCount,plural,one{I am aware that I am removing a gift card with balance} other{I am aware that I am removing gift cards with balance}}" }, - "src_dot_giftCards_dot_components_dot_GiftCardExpirySelect_dot_expiryDateLabel": { - "context": "GiftCarUpdateDetailsExpirySection expiry date label", - "string": "Expiration date" + "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_defaultDescription": { + "context": "GiftCardDeleteDialog default description", + "string": "{selectedItemsCount,plural,one{Are you sure you want to delete this gift card?} other{Are you sure you want to delete {selectedItemsCount} giftCards?}}" }, - "src_dot_giftCards_dot_components_dot_GiftCardExpirySelect_dot_expiryPeriodLabel": { - "context": "GiftCarUpdateDetailsExpirySection expiry period label", - "string": "Expiry period" + "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_deleteSuccessAlertText": { + "context": "GiftCardDeleteDialog success alert text", + "string": "{selectedItemsCount,plural,one{Successfully deleted gift card} other{Successfully deleted gift cards}}" }, - "src_dot_giftCards_dot_components_dot_GiftCardExpirySelect_dot_neverExpireLabel": { - "context": "GiftCarUpdateDetailsExpirySection never expire label", - "string": "Never expire" + "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_title": { + "context": "GiftCardDeleteDialog single title", + "string": "{selectedItemsCount,plural,one{Delete Gift Card} other{Delete Gift Cards}}" + }, + "src_dot_giftCards_dot_components_dot_GiftCardDeleteDialog_dot_withBalanceDescription": { + "context": "GiftCardDeleteDialog with balance description", + "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": { "context": "GiftCardTagInput tag label", @@ -3605,15 +3752,19 @@ }, "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_dayLabel": { "context": "TimePeriodTextWithSelectField day label", - "string": "days after usage" + "string": "days after issue" }, "src_dot_giftCards_dot_components_dot_TimePeriodField_dot_monthLabel": { "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": { "context": "TimePeriodTextWithSelectField year label", - "string": "years after usage" + "string": "years after issue" }, "src_dot_home": { "context": "home section name", @@ -4411,6 +4562,10 @@ "context": "order 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": { "context": "order payment", "string": "Preauthorized amount" @@ -4752,6 +4907,14 @@ "context": "section header", "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": { "context": "checkbox label description", "string": "All orders will be automatically confirmed and all payments will be captured." @@ -5402,7 +5565,24 @@ "context": "switch button", "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" }, "src_dot_productTypes_dot_components_dot_ProductTypeListPage_dot_1776073799": { @@ -7594,6 +7774,10 @@ "context": "event", "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": { "context": "event", "string": "All events" @@ -7690,6 +7874,10 @@ "context": "event", "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": { "context": "event", "string": "Authorize payment" diff --git a/schema.graphql b/schema.graphql index 9d22bffba..0bdc6a9e1 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1111,6 +1111,7 @@ enum CheckoutErrorCode { TAX_ERROR UNIQUE VOUCHER_NOT_APPLICABLE + GIFT_CARD_NOT_APPLICABLE ZERO_QUANTITY MISSING_CHANNEL_SLUG CHANNEL_INACTIVE @@ -2253,7 +2254,6 @@ type GiftCard implements Node & ObjectWithMetadata { code: String! isActive: Boolean! expiryDate: Date - expiryType: GiftCardExpiryTypeEnum! tag: String created: DateTime! lastUsedOn: DateTime @@ -2268,9 +2268,9 @@ type GiftCard implements Node & ObjectWithMetadata { createdByEmail: String usedByEmail: String app: App - expiryPeriod: TimePeriod product: Product events: [GiftCardEvent!]! + boughtInChannel: String 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.") startDate: DateTime @deprecated(reason: "This field will be removed in Saleor 4.0.") @@ -2282,6 +2282,31 @@ type GiftCardActivate { 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 { pageInfo: PageInfo! edges: [GiftCardCountableEdge!]! @@ -2301,11 +2326,13 @@ type GiftCardCreate { input GiftCardCreateInput { tag: String + expiryDate: Date startDate: Date endDate: Date balance: PriceInput! userEmail: String - expirySettings: GiftCardExpirySettingsInput! + channel: String + isActive: Boolean! code: String note: String } @@ -2350,25 +2377,17 @@ type GiftCardEvent implements Node { tag: String oldTag: String balance: GiftCardEventBalance - expiry: GiftCardEventExpiry + expiryDate: Date + oldExpiryDate: Date } type GiftCardEventBalance { - initialBalance: Money! + initialBalance: Money currentBalance: Money! oldInitialBalance: Money oldCurrentBalance: Money } -type GiftCardEventExpiry { - expiryType: GiftCardExpiryTypeEnum - expiryPeriod: TimePeriod - expiryDate: Date - oldExpiryType: GiftCardExpiryTypeEnum - oldExpiryPeriod: TimePeriod - oldExpiryDate: Date -} - enum GiftCardEventsEnum { ISSUED BOUGHT @@ -2376,25 +2395,77 @@ enum GiftCardEventsEnum { ACTIVATED DEACTIVATED BALANCE_RESET - EXPIRY_SETTINGS_UPDATED + EXPIRY_DATE_UPDATED SENT_TO_CUSTOMER RESENT -} - -input GiftCardExpirySettingsInput { - expiryType: GiftCardExpiryTypeEnum! - expiryDate: Date - expiryPeriod: TimePeriodInputType -} - -enum GiftCardExpiryTypeEnum { - NEVER_EXPIRE - EXPIRY_PERIOD - EXPIRY_DATE + NOTE_ADDED + USED_IN_ORDER } input GiftCardFilterInput { + isActive: Boolean 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 { @@ -2405,10 +2476,10 @@ type GiftCardUpdate { input GiftCardUpdateInput { tag: String + expiryDate: Date startDate: Date endDate: Date balanceAmount: PositiveDecimal - expirySettings: GiftCardExpirySettingsInput } type Group implements Node { @@ -3645,6 +3716,7 @@ type Mutation { shopSettingsTranslate(input: ShopSettingsTranslationInput!, languageCode: LanguageCodeEnum!): ShopSettingsTranslate shopAddressUpdate(input: AddressInput): ShopAddressUpdate orderSettingsUpdate(input: OrderSettingsUpdateInput!): OrderSettingsUpdate + giftCardSettingsUpdate(input: GiftCardSettingsUpdateInput!): GiftCardSettingsUpdate shippingMethodChannelListingUpdate(id: ID!, input: ShippingMethodChannelListingInput!): ShippingMethodChannelListingUpdate shippingPriceCreate(input: ShippingPriceInput!): ShippingPriceCreate shippingPriceDelete(id: ID!): ShippingPriceDelete @@ -3783,6 +3855,11 @@ type Mutation { giftCardDelete(id: ID!): GiftCardDelete giftCardDeactivate(id: ID!): GiftCardDeactivate 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 externalNotificationTrigger(channel: String!, input: ExternalNotificationTriggerInput!, pluginId: String): ExternalNotificationTrigger saleCreate(input: SaleInput!): SaleCreate @@ -4383,6 +4460,7 @@ input OrderReturnProductsInput { type OrderSettings { automaticallyConfirmAllNewOrders: Boolean! + automaticallyFulfillNonShippableGiftCard: Boolean! } type OrderSettingsError { @@ -4402,7 +4480,8 @@ type OrderSettingsUpdate { } input OrderSettingsUpdateInput { - automaticallyConfirmAllNewOrders: Boolean! + automaticallyConfirmAllNewOrders: Boolean + automaticallyFulfillNonShippableGiftCard: Boolean } enum OrderSortField { @@ -5235,6 +5314,7 @@ input ProductFilterInput { price: PriceRangeInput minimalPrice: PriceRangeInput productTypes: [ID] + giftCard: Boolean ids: [ID] channel: String } @@ -5397,6 +5477,7 @@ type ProductType implements Node & ObjectWithMetadata { weight: Weight privateMetadata: [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.") taxType: TaxType variantAttributes(variantSelection: VariantAttributeScope): [Attribute] @@ -5448,12 +5529,14 @@ input ProductTypeFilterInput { configurable: ProductTypeConfigurable productType: ProductTypeEnum metadata: [MetadataFilter] + kind: ProductTypeKindEnum ids: [ID] } input ProductTypeInput { name: String slug: String + kind: ProductTypeKindEnum hasVariants: Boolean productAttributes: [ID] variantAttributes: [ID] @@ -5463,6 +5546,11 @@ input ProductTypeInput { taxCode: String } +enum ProductTypeKindEnum { + NORMAL + GIFT_CARD +} + type ProductTypeReorderAttributes { productType: ProductType 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 shop: Shop! orderSettings: OrderSettings + giftCardSettings: GiftCardSettings! shippingZone(id: ID!, channel: String): ShippingZone shippingZones(filter: ShippingZoneFilterInput, channel: String, before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection digitalContent(id: ID!): DigitalContent @@ -5714,7 +5803,7 @@ type Query { menuItem(id: ID!, channel: String): MenuItem menuItems(channel: String, sortBy: MenuItemSortingInput, filter: MenuItemFilterInput, before: String, after: String, first: Int, last: Int): MenuItemCountableConnection 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 plugins(filter: PluginFilterInput, sortBy: PluginSortingInput, before: String, after: String, first: Int, last: Int): PluginCountableConnection sale(id: ID!, channel: String): Sale @@ -6471,6 +6560,7 @@ input TimePeriodInputType { enum TimePeriodTypeEnum { DAY + WEEK MONTH YEAR } diff --git a/src/components/ActionDialog/ActionDialog.tsx b/src/components/ActionDialog/ActionDialog.tsx index afe009f34..f33d7d998 100644 --- a/src/components/ActionDialog/ActionDialog.tsx +++ b/src/components/ActionDialog/ActionDialog.tsx @@ -6,7 +6,7 @@ import { ConfirmButtonTransitionState } from "../ConfirmButton"; import DialogButtons from "./DialogButtons"; import { ActionDialogVariant, Size } from "./types"; -interface ActionDialogProps extends DialogProps { +export interface ActionDialogProps extends DialogProps { children?: React.ReactNode; confirmButtonLabel?: string; confirmButtonState: ConfirmButtonTransitionState; diff --git a/src/components/AppLayout/AppChannelContext.tsx b/src/components/AppLayout/AppChannelContext.tsx index 7b6f820f7..94f5bc0e3 100644 --- a/src/components/AppLayout/AppChannelContext.tsx +++ b/src/components/AppLayout/AppChannelContext.tsx @@ -57,8 +57,7 @@ export const AppChannelProvider: React.FC = ({ children }) => { const availableChannels = channelData?.channels || []; const channel = - channelData && - (availableChannels.find(channel => channel.id === selectedChannel) || null); + channelData && (availableChannels.find(getById(selectedChannel)) || null); return ( = ({ publicationDate, ...(visibleInListings !== undefined ? { visibleInListings } : {}) }; - const dateNow = React.useContext(DateContext); + const dateNow = useCurrentDate(); const localizeDate = useDateLocalize(); const hasAvailableProps = isAvailable !== undefined && availableForPurchase !== undefined; diff --git a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx index 4153fcb76..d601066df 100644 --- a/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx +++ b/src/components/ExtendedPageHeader/ExtendedPageHeader.tsx @@ -1,3 +1,4 @@ +import { Divider } from "@material-ui/core"; import { makeStyles } from "@saleor/macaw-ui"; import classNames from "classnames"; import React from "react"; @@ -17,6 +18,9 @@ const useStyles = makeStyles( } } }, + underline: { + marginBottom: theme.spacing(4) + }, grid: { padding: theme.spacing(2) }, @@ -51,25 +55,34 @@ interface ExtendedPageHeaderProps { children?: React.ReactNode; className?: string; inline?: boolean; + underline?: boolean; title?: React.ReactNode; testId?: string; } const ExtendedPageHeader: React.FC = props => { - const { children, className, inline, title, testId } = props; + const { children, className, inline, underline, title, testId } = props; const classes = useStyles(props); return ( -
- {title} -
{children}
-
+ <> +
+ {title} +
{children}
+
+ {underline && ( +
+ +
+ )} + ); }; ExtendedPageHeader.displayName = "ExtendedPageHeader"; diff --git a/src/components/PageHeader/PageHeader.tsx b/src/components/PageHeader/PageHeader.tsx index 3119c990f..5406c52f6 100644 --- a/src/components/PageHeader/PageHeader.tsx +++ b/src/components/PageHeader/PageHeader.tsx @@ -43,12 +43,13 @@ interface PageHeaderProps { children?: React.ReactNode; className?: string; inline?: boolean; + underline?: boolean; limitText?: string; title?: React.ReactNode; } const PageHeader: React.FC = props => { - const { children, className, inline, limitText, title } = props; + const { children, className, inline, underline, limitText, title } = props; const classes = useStyles(props); @@ -57,6 +58,7 @@ const PageHeader: React.FC = props => { testId="page-header" className={className} inline={inline} + underline={underline} title={ {title !== undefined ? title : } diff --git a/src/components/RadioGroupField/RadioGroupField.tsx b/src/components/RadioGroupField/RadioGroupField.tsx index f9158baa9..a847fbc2c 100644 --- a/src/components/RadioGroupField/RadioGroupField.tsx +++ b/src/components/RadioGroupField/RadioGroupField.tsx @@ -7,47 +7,11 @@ import { Radio, RadioGroup } from "@material-ui/core"; -import { makeStyles } from "@saleor/macaw-ui"; import classNames from "classnames"; import React from "react"; import { FormattedMessage } from "react-intl"; -const useStyles = makeStyles( - 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" - } -); +import { useStyles } from "./styles"; export interface RadioGroupFieldChoice< T extends string | number = string | number @@ -119,6 +83,9 @@ export const RadioGroupField: React.FC = props => { [classes.radioLabel]: variant !== "inline", [classes.radioLabelInline]: variant === "inline" })} + classes={{ + label: classes.label + }} control={ ({ + 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" + } +); diff --git a/src/components/TypeDeleteWarningDialog/DeleteWarningDialogConsentContent.tsx b/src/components/TypeDeleteWarningDialog/DeleteWarningDialogConsentContent.tsx new file mode 100644 index 000000000..8125c398b --- /dev/null +++ b/src/components/TypeDeleteWarningDialog/DeleteWarningDialogConsentContent.tsx @@ -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 = ({ + description, + consentLabel, + isConsentChecked, + onConsentChange +}) => { + const classes = useStyles(); + + const handleConsentChange = ({ target }: ChangeEvent) => + onConsentChange(target.value); + + return ( + <> + {description} + + {consentLabel && ( + + {consentLabel} + + } + /> + )} + + ); +}; + +export default DeleteWarningDialogConsentContent; diff --git a/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialogContent.tsx b/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialogContent.tsx index 9645a0018..c3dd13d94 100644 --- a/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialogContent.tsx +++ b/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialogContent.tsx @@ -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 CardSpacer from "@saleor/components/CardSpacer"; 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 React, { ChangeEvent, useState } from "react"; +import React, { useState } from "react"; import { MessageDescriptor, useIntl } from "react-intl"; +import DeleteButton from "../DeleteButton"; +import DeleteWarningDialogConsentContent from "./DeleteWarningDialogConsentContent"; import { useTypeDeleteWarningDialogStyles as useStyles } from "./styles"; interface TypeDeleteWarningDialogContentProps { @@ -40,9 +40,6 @@ const TypeDeleteWarningDialogContent: React.FC) => - setIsConsentChecked(target.value); - const handleViewAssignedItems = () => navigate(viewAssignedItemsUrl); const isDisbled = hasAssignedItems ? !isConsentChecked : false; @@ -52,26 +49,16 @@ const TypeDeleteWarningDialogContent: React.FC - - {intl.formatMessage(description, { + {chunks} })} - - - {consentLabel && ( - - {intl.formatMessage(consentLabel)} - - } - /> - )} + consentLabel={consentLabel && intl.formatMessage(consentLabel)} + isConsentChecked={isConsentChecked} + onConsentChange={setIsConsentChecked} + />
{shouldShowViewAssignedItemsButton && ( diff --git a/src/components/VisibilityCard/VisibilityCard.tsx b/src/components/VisibilityCard/VisibilityCard.tsx index 494118cca..3f3b9634a 100644 --- a/src/components/VisibilityCard/VisibilityCard.tsx +++ b/src/components/VisibilityCard/VisibilityCard.tsx @@ -3,6 +3,7 @@ import CardTitle from "@saleor/components/CardTitle"; import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import Hr from "@saleor/components/Hr"; import RadioSwitchField from "@saleor/components/RadioSwitchField"; +import useCurrentDate from "@saleor/hooks/useCurrentDate"; import useDateLocalize from "@saleor/hooks/useDateLocalize"; import { ChangeEvent } from "@saleor/hooks/useForm"; import { makeStyles } from "@saleor/macaw-ui"; @@ -12,7 +13,6 @@ import classNames from "classnames"; import React from "react"; import { useIntl } from "react-intl"; -import { DateContext } from "../Date/DateContext"; import FormSpacer from "../FormSpacer"; import DateVisibilitySelector from "./DateVisibilitySelector"; @@ -103,7 +103,7 @@ export const VisibilityCard: React.FC = props => { const classes = useStyles(props); const intl = useIntl(); const localizeDate = useDateLocalize(); - const dateNow = React.useContext(DateContext); + const dateNow = useCurrentDate(); const hasAvailableProps = isAvailable !== undefined && availableForPurchase !== undefined; diff --git a/src/fragments/errors.ts b/src/fragments/errors.ts index 7feab41cf..af921c3d8 100644 --- a/src/fragments/errors.ts +++ b/src/fragments/errors.ts @@ -222,3 +222,10 @@ export const giftCardErrorFragment = gql` field } `; + +export const giftCardSettingsErrorFragment = gql` + fragment GiftCardSettingsErrorFragment on GiftCardSettingsError { + code + field + } +`; diff --git a/src/fragments/giftCards.ts b/src/fragments/giftCards.ts new file mode 100644 index 000000000..518d56859 --- /dev/null +++ b/src/fragments/giftCards.ts @@ -0,0 +1,11 @@ +import gql from "graphql-tag"; + +export const fragmentGiftCardsSettings = gql` + fragment GiftCardsSettingsFragment on GiftCardSettings { + expiryType + expiryPeriod { + type + amount + } + } +`; diff --git a/src/fragments/orders.ts b/src/fragments/orders.ts index 7ae6a5086..c9a865f33 100644 --- a/src/fragments/orders.ts +++ b/src/fragments/orders.ts @@ -182,6 +182,27 @@ export const fragmentOrderDetails = gql` billingAddress { ...AddressFragment } + giftCards { + events { + id + type + orderId + balance { + initialBalance { + ...Money + } + currentBalance { + ...Money + } + oldInitialBalance { + ...Money + } + oldCurrentBalance { + ...Money + } + } + } + } isShippingRequired canFinalize created @@ -296,6 +317,7 @@ export const fragmentOrderDetails = gql` export const fragmentOrderSettings = gql` fragment OrderSettingsFragment on OrderSettings { automaticallyConfirmAllNewOrders + automaticallyFulfillNonShippableGiftCard } `; diff --git a/src/fragments/productTypes.ts b/src/fragments/productTypes.ts index 981dbe571..7163b63f1 100644 --- a/src/fragments/productTypes.ts +++ b/src/fragments/productTypes.ts @@ -7,6 +7,7 @@ export const productTypeFragment = gql` fragment ProductTypeFragment on ProductType { id name + kind hasVariants isShippingRequired taxType { diff --git a/src/fragments/types/GiftCardSettingsErrorFragment.ts b/src/fragments/types/GiftCardSettingsErrorFragment.ts new file mode 100644 index 000000000..627b2b047 --- /dev/null +++ b/src/fragments/types/GiftCardSettingsErrorFragment.ts @@ -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; +} diff --git a/src/fragments/types/GiftCardsSettingsFragment.ts b/src/fragments/types/GiftCardsSettingsFragment.ts new file mode 100644 index 000000000..8db830c45 --- /dev/null +++ b/src/fragments/types/GiftCardsSettingsFragment.ts @@ -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; +} diff --git a/src/fragments/types/OrderDetailsFragment.ts b/src/fragments/types/OrderDetailsFragment.ts index 064193c19..92511e824 100644 --- a/src/fragments/types/OrderDetailsFragment.ts +++ b/src/fragments/types/OrderDetailsFragment.ts @@ -3,7 +3,7 @@ // @generated // 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 @@ -43,6 +43,51 @@ export interface OrderDetailsFragment_billingAddress { 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 { __typename: "Money"; amount: number; @@ -497,6 +542,7 @@ export interface OrderDetailsFragment { metadata: (OrderDetailsFragment_metadata | null)[]; privateMetadata: (OrderDetailsFragment_privateMetadata | null)[]; billingAddress: OrderDetailsFragment_billingAddress | null; + giftCards: (OrderDetailsFragment_giftCards | null)[] | null; isShippingRequired: boolean; canFinalize: boolean; created: any; diff --git a/src/fragments/types/OrderSettingsFragment.ts b/src/fragments/types/OrderSettingsFragment.ts index 262f0d55c..dcc3309b3 100644 --- a/src/fragments/types/OrderSettingsFragment.ts +++ b/src/fragments/types/OrderSettingsFragment.ts @@ -10,4 +10,5 @@ export interface OrderSettingsFragment { __typename: "OrderSettings"; automaticallyConfirmAllNewOrders: boolean; + automaticallyFulfillNonShippableGiftCard: boolean; } diff --git a/src/fragments/types/ProductTypeDetailsFragment.ts b/src/fragments/types/ProductTypeDetailsFragment.ts index 4a8909e27..ef929cac3 100644 --- a/src/fragments/types/ProductTypeDetailsFragment.ts +++ b/src/fragments/types/ProductTypeDetailsFragment.ts @@ -3,7 +3,7 @@ // @generated // 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 @@ -63,6 +63,7 @@ export interface ProductTypeDetailsFragment { __typename: "ProductType"; id: string; name: string; + kind: ProductTypeKindEnum; hasVariants: boolean; isShippingRequired: boolean; taxType: ProductTypeDetailsFragment_taxType | null; diff --git a/src/fragments/types/ProductTypeFragment.ts b/src/fragments/types/ProductTypeFragment.ts index c6aac0fb4..46f0d4a6f 100644 --- a/src/fragments/types/ProductTypeFragment.ts +++ b/src/fragments/types/ProductTypeFragment.ts @@ -3,6 +3,8 @@ // @generated // This file was automatically generated and should not be edited. +import { ProductTypeKindEnum } from "./../../types/globalTypes"; + // ==================================================== // GraphQL fragment: ProductTypeFragment // ==================================================== @@ -17,6 +19,7 @@ export interface ProductTypeFragment { __typename: "ProductType"; id: string; name: string; + kind: ProductTypeKindEnum; hasVariants: boolean; isShippingRequired: boolean; taxType: ProductTypeFragment_taxType | null; diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx index b50869ed4..eaf6ffe37 100644 --- a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialog.tsx @@ -1,11 +1,14 @@ import { Dialog, DialogTitle } from "@material-ui/core"; import { IMessage } from "@saleor/components/messages"; +import useCurrentDate from "@saleor/hooks/useCurrentDate"; import useNotifier from "@saleor/hooks/useNotifier"; import { GiftCardCreateInput } from "@saleor/types/globalTypes"; import commonErrorMessages from "@saleor/utils/errors/common"; +import { DialogActionHandlersProps } from "@saleor/utils/handlers/dialogActionHandlers"; import React, { useState } from "react"; import { useIntl } from "react-intl"; +import { GIFT_CARD_LIST_QUERY } from "../GiftCardsList/types"; import ContentWithProgress from "./ContentWithProgress"; import GiftCardCreateDialogCodeContent from "./GiftCardCreateDialogCodeContent"; import GiftCardCreateDialogForm, { @@ -15,15 +18,10 @@ import { giftCardCreateDialogMessages as messages } from "./messages"; import { useGiftCardCreateMutation } from "./mutations"; import { useChannelCurrencies } from "./queries"; import { GiftCardCreate } from "./types/GiftCardCreate"; -import { getGiftCardExpirySettingsInputData } from "./utils"; +import { getGiftCardExpiryInputData } from "./utils"; -interface GiftCardCreateDialogProps { - onClose: () => void; - open: boolean; -} - -const GiftCardCreateDialog: React.FC = ({ - onClose, +const GiftCardCreateDialog: React.FC = ({ + closeDialog, open }) => { const intl = useIntl(); @@ -56,6 +54,8 @@ const GiftCardCreateDialog: React.FC = ({ } }; + const currentDate = useCurrentDate(); + const getParsedSubmitInputData = ( formData: GiftCardCreateFormData ): GiftCardCreateInput => { @@ -64,23 +64,29 @@ const GiftCardCreateDialog: React.FC = ({ balanceCurrency, note, tag, - selectedCustomer + sendToCustomerSelected, + selectedCustomer, + requiresActivation, + channelSlug } = formData; return { note: note || null, tag: tag || null, - userEmail: selectedCustomer.email || null, + userEmail: (sendToCustomerSelected && selectedCustomer.email) || null, + channel: (sendToCustomerSelected && channelSlug) || null, balance: { amount: balanceAmount, currency: balanceCurrency }, - expirySettings: getGiftCardExpirySettingsInputData(formData) + expiryDate: getGiftCardExpiryInputData(formData, currentDate), + isActive: !requiresActivation }; }; const [createGiftCard, createGiftCardOpts] = useGiftCardCreateMutation({ - onCompleted + onCompleted, + refetchQueries: [GIFT_CARD_LIST_QUERY] }); const handleSubmit = (data: GiftCardCreateFormData) => { @@ -92,7 +98,7 @@ const GiftCardCreateDialog: React.FC = ({ }; const handleClose = () => { - onClose(); + closeDialog(); // dialog closing animation runs slower than prop change // and we don't want to show the form for a split second setTimeout(() => setCardCode(null), 0); diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx index a6568eda8..a7bbedb7f 100644 --- a/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateDialogForm.tsx @@ -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 DialogButtons from "@saleor/components/ActionDialog/DialogButtons"; import CardSpacer from "@saleor/components/CardSpacer"; +import ControlledCheckbox from "@saleor/components/ControlledCheckbox"; import TextWithSelectField from "@saleor/components/TextWithSelectField"; import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; -import GiftCardExpirySelect from "@saleor/giftCards/components/GiftCardExpirySelect"; import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput"; import useForm from "@saleor/hooks/useForm"; import { commonMessages } from "@saleor/intl"; import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; import Label from "@saleor/orders/components/OrderHistory/Label"; import { - GiftCardExpiryTypeEnum, + GiftCardSettingsExpiryTypeEnum, TimePeriodTypeEnum } from "@saleor/types/globalTypes"; import { getFormErrors } from "@saleor/utils/errors"; import { mapSingleValueNodeToChoice } from "@saleor/utils/maps"; 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 GiftCardCustomerSelectField from "./GiftCardCustomerSelectField"; +import GiftCardCreateExpirySelect from "./GiftCardCreateExpirySelect"; import { giftCardCreateDialogMessages as messages } from "./messages"; import { useGiftCardCreateDialogFormStyles as useStyles } from "./styles"; -import { GiftCardCommonFormData, GiftCardCreateFormCustomer } from "./types"; +import { + GiftCardCommonFormData, + GiftCardCreateFormCustomer, + GiftCardExpiryType +} from "./types"; export interface GiftCardCreateFormData extends GiftCardCommonFormData { note: string; + sendToCustomerSelected: boolean; selectedCustomer?: GiftCardCreateFormCustomer; + channelSlug?: string; + expirySelected: boolean; + expiryType: GiftCardExpiryType; + expiryPeriodType: TimePeriodTypeEnum; + expiryPeriodAmount: number; + requiresActivation: boolean; } const initialCustomer = { email: "", name: "" }; @@ -37,10 +55,13 @@ export const initialData: GiftCardCreateFormData = { balanceAmount: 1, balanceCurrency: null, note: "", + sendToCustomerSelected: false, + expirySelected: false, + expiryType: "EXPIRY_PERIOD", expiryDate: "", - expiryType: GiftCardExpiryTypeEnum.EXPIRY_PERIOD, - expiryPeriodType: TimePeriodTypeEnum.YEAR, - expiryPeriodAmount: 1 + expiryPeriodType: TimePeriodTypeEnum.MONTH, + expiryPeriodAmount: 12, + requiresActivation: true }; interface GiftCardCreateDialogFormProps { @@ -63,6 +84,11 @@ const GiftCardCreateDialogForm: React.FC = ({ const initialCurrency = channelCurrencies[0]; + const { + data: settingsData, + loading: loadingSettings + } = useGiftCardSettingsQuery(); + const [selectedCustomer, setSelectedCustomer] = useState< GiftCardCreateFormCustomer >(initialCustomer); @@ -70,33 +96,65 @@ const GiftCardCreateDialogForm: React.FC = ({ const handleSubmit = (data: GiftCardCreateFormData) => onSubmit({ ...data, selectedCustomer }); + const getInitialExpirySettingsData = (): Partial => { + 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( - { ...initialData, balanceCurrency: initialCurrency }, + { + ...initialData, + ...getInitialExpirySettingsData(), + balanceCurrency: initialCurrency, + channelSlug: "" + }, handleSubmit ); const formErrors = getFormErrors( - [ - "tag", - "expiryDate", - "expiryPeriod", - "customer", - "currency", - "amount", - "balance" - ], + ["tag", "expiryDate", "customer", "currency", "amount", "balance"], apiErrors ); const { tag, + sendToCustomerSelected, + channelSlug, + balanceAmount, + balanceCurrency, + expirySelected, + expiryType, expiryPeriodAmount, expiryPeriodType, - expiryType, - balanceAmount, - balanceCurrency + expiryDate, + requiresActivation } = data; + const shouldEnableSubmitButton = () => { + if (!balanceAmount) { + return false; + } + + if (expirySelected && expiryType === "EXPIRY_DATE") { + return !!expiryDate; + } + + return true; + }; + return ( <> @@ -107,7 +165,7 @@ const GiftCardCreateDialogForm: React.FC = ({ choices={mapSingleValueNodeToChoice(channelCurrencies)} containerClassName={classes.balanceContainer} textFieldProps={{ - type: "number", + type: "float", label: intl.formatMessage(messages.amountLabel), name: "balanceAmount", value: balanceAmount, @@ -128,24 +186,25 @@ const GiftCardCreateDialogForm: React.FC = ({ /> - - - - ; + expiryDate?: string; +} + +const GiftCardCreateExpirySelect: React.FC = ({ + 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 ( + <> + + {expirySelected && ( + <> + + + + + {expiryType === "EXPIRY_DATE" && ( + + )} + + {expiryType === "EXPIRY_PERIOD" && ( +
+ + +
+ + + + + {getExpiryPeriodTerminationDate( + currentDate, + expiryPeriodType, + expiryPeriodAmount + )?.format("L")} + +
+
+ )} + + + )} + + ); +}; + +export default GiftCardCreateExpirySelect; diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/index.tsx b/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/index.tsx new file mode 100644 index 000000000..97cb3bc71 --- /dev/null +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/index.tsx @@ -0,0 +1,2 @@ +export * from "./GiftCardCreateExpirySelect"; +export { default } from "./GiftCardCreateExpirySelect"; diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/messages.ts b/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/messages.ts new file mode 100644 index 000000000..d8f496565 --- /dev/null +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/messages.ts @@ -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" + } +}); diff --git a/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/styles.ts b/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/styles.ts new file mode 100644 index 000000000..71979fd2b --- /dev/null +++ b/src/giftCards/GiftCardCreateDialog/GiftCardCreateExpirySelect/styles.ts @@ -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" } +); diff --git a/src/giftCards/GiftCardCreateDialog/messages.ts b/src/giftCards/GiftCardCreateDialog/messages.ts index cecf392b8..d40c16e0f 100644 --- a/src/giftCards/GiftCardCreateDialog/messages.ts +++ b/src/giftCards/GiftCardCreateDialog/messages.ts @@ -17,11 +17,6 @@ export const giftCardCreateDialogMessages = defineMessages({ defaultMessage: "Customer", 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: { defaultMessage: "Note", description: "GiftCardCreateDialog note label" @@ -46,5 +41,13 @@ export const giftCardCreateDialogMessages = defineMessages({ createdSuccessAlertTitle: { defaultMessage: "Successfully created gift card", 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" } }); diff --git a/src/giftCards/GiftCardCreateDialog/types.ts b/src/giftCards/GiftCardCreateDialog/types.ts index 06c28169c..f96c1cd3a 100644 --- a/src/giftCards/GiftCardCreateDialog/types.ts +++ b/src/giftCards/GiftCardCreateDialog/types.ts @@ -1,9 +1,9 @@ import { GiftCardError } from "@saleor/fragments/types/GiftCardError"; import { FormChange } from "@saleor/hooks/useForm"; -import { - GiftCardExpiryTypeEnum, - TimePeriodTypeEnum -} from "@saleor/types/globalTypes"; + +import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm"; + +export type GiftCardExpiryType = "EXPIRY_DATE" | "EXPIRY_PERIOD"; export interface GiftCardCreateFormCustomer { name: string; @@ -15,16 +15,22 @@ export interface GiftCardCommonFormData { balanceAmount: number; balanceCurrency: string; expiryDate: string; - expiryType: GiftCardExpiryTypeEnum; - expiryPeriodType: TimePeriodTypeEnum; - expiryPeriodAmount: number; } export type GiftCardCreateFormErrors = Record< - "tag" | "expiryDate" | "expiryPeriod" | "customer" | "currency" | "amount", + "tag" | "expiryDate" | "customer" | "currency" | "amount", GiftCardError >; +export type GiftCardCreateInputData = Pick< + GiftCardCreateFormData, + | "expirySelected" + | "expiryDate" + | "expiryPeriodAmount" + | "expiryPeriodType" + | "expiryType" +>; + export interface GiftCardCreateFormCommonProps { change: FormChange; errors: GiftCardCreateFormErrors; diff --git a/src/giftCards/GiftCardCreateDialog/utils.ts b/src/giftCards/GiftCardCreateDialog/utils.ts index e796ab666..7cb98d267 100644 --- a/src/giftCards/GiftCardCreateDialog/utils.ts +++ b/src/giftCards/GiftCardCreateDialog/utils.ts @@ -1,41 +1,54 @@ -import { - GiftCardExpirySettingsInput, - GiftCardExpiryTypeEnum -} from "@saleor/types/globalTypes"; +import { TimePeriodTypeEnum } from "@saleor/types/globalTypes"; +import moment from "moment-timezone"; -import { GiftCardCommonFormData } from "./types"; +import { GiftCardCreateFormData } from "./GiftCardCreateDialogForm"; -export const getGiftCardExpirySettingsInputData = ({ - expiryType, - expiryDate, - expiryPeriodAmount, - expiryPeriodType -}: Pick< - GiftCardCommonFormData, - "expiryDate" | "expiryPeriodAmount" | "expiryPeriodType" | "expiryType" ->): GiftCardExpirySettingsInput => { - switch (expiryType) { - case GiftCardExpiryTypeEnum.EXPIRY_DATE: { - return { - expiryType, - expiryDate - }; - } +const addToCurrentDate = ( + currentDate: number, + expiryPeriodAmount: number, + unit: moment.unitOfTime.DurationConstructor +) => moment(currentDate).add(expiryPeriodAmount, unit); - case GiftCardExpiryTypeEnum.EXPIRY_PERIOD: { - return { - expiryType, - expiryPeriod: { - amount: expiryPeriodAmount, - type: expiryPeriodType - } - }; - } - - default: { - return { - expiryType - }; - } +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, + expiryDate, + expiryPeriodAmount, + expiryPeriodType + }: GiftCardCreateFormData, + currentDate: number +): string => { + if (!expirySelected) { + return; + } + + if (expiryType === "EXPIRY_PERIOD") { + return getExpiryPeriodTerminationDate( + currentDate, + expiryPeriodType, + expiryPeriodAmount + )?.format("YYYY-MM-DD"); + } + + return expiryDate; +}; diff --git a/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/GiftCardExpirySettingsCard.tsx b/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/GiftCardExpirySettingsCard.tsx new file mode 100644 index 000000000..b125ff463 --- /dev/null +++ b/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/GiftCardExpirySettingsCard.tsx @@ -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 { + data: GiftCardSettingsFormData; + disabled: boolean; + onChange: (event: React.ChangeEvent) => void; +} + +const GiftCardExpirySettingsCard: React.FC = ({ + data, + disabled, + errors, + onChange +}) => { + const intl = useIntl(); + + return ( + + + + + + + ); +}; + +GiftCardExpirySettingsCard.displayName = "GiftCardExpirySettingsCard"; +export default GiftCardExpirySettingsCard; diff --git a/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/index.ts b/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/index.ts new file mode 100644 index 000000000..12f529fb4 --- /dev/null +++ b/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/index.ts @@ -0,0 +1,2 @@ +export * from "./GiftCardExpirySettingsCard"; +export { default } from "./GiftCardExpirySettingsCard"; diff --git a/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/messages.ts b/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/messages.ts new file mode 100644 index 000000000..22c8beb4e --- /dev/null +++ b/src/giftCards/GiftCardSettings/GiftCardExpirySettingsCard/messages.ts @@ -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" + } +}); diff --git a/src/giftCards/GiftCardSettings/GiftCardSettingsPage.tsx b/src/giftCards/GiftCardSettings/GiftCardSettingsPage.tsx new file mode 100644 index 000000000..41877639c --- /dev/null +++ b/src/giftCards/GiftCardSettings/GiftCardSettingsPage.tsx @@ -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 ( + + + {intl.formatMessage(sectionNames.giftCards)} + + +
+ {({ data: formData, submit, hasChanged, change }) => ( + +
+ + + +
+ + +
+ )} +
+
+ ); +}; + +export default GiftCardSettingsPage; diff --git a/src/giftCards/GiftCardSettings/index.tsx b/src/giftCards/GiftCardSettings/index.tsx new file mode 100644 index 000000000..0a83f0787 --- /dev/null +++ b/src/giftCards/GiftCardSettings/index.tsx @@ -0,0 +1,2 @@ +export * from "./GiftCardSettingsPage"; +export { default } from "./GiftCardSettingsPage"; diff --git a/src/giftCards/GiftCardSettings/messages.ts b/src/giftCards/GiftCardSettings/messages.ts new file mode 100644 index 000000000..e94951375 --- /dev/null +++ b/src/giftCards/GiftCardSettings/messages.ts @@ -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 | undefined, + intl: IntlShape +): string { + return getCommonFormFieldErrorMessage(error, intl); +} diff --git a/src/giftCards/GiftCardSettings/mutations.ts b/src/giftCards/GiftCardSettings/mutations.ts new file mode 100644 index 000000000..dcf29b7f5 --- /dev/null +++ b/src/giftCards/GiftCardSettings/mutations.ts @@ -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); diff --git a/src/giftCards/GiftCardSettings/queries.ts b/src/giftCards/GiftCardSettings/queries.ts new file mode 100644 index 000000000..4bc265108 --- /dev/null +++ b/src/giftCards/GiftCardSettings/queries.ts @@ -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 +); diff --git a/src/giftCards/GiftCardSettings/types.ts b/src/giftCards/GiftCardSettings/types.ts new file mode 100644 index 000000000..093eb6f41 --- /dev/null +++ b/src/giftCards/GiftCardSettings/types.ts @@ -0,0 +1,7 @@ +import { TimePeriodTypeEnum } from "@saleor/types/globalTypes"; + +export interface GiftCardSettingsFormData { + expiryPeriodActive: boolean; + expiryPeriodType: TimePeriodTypeEnum; + expiryPeriodAmount: number; +} diff --git a/src/giftCards/GiftCardSettings/types/GiftCardSettings.ts b/src/giftCards/GiftCardSettings/types/GiftCardSettings.ts new file mode 100644 index 000000000..846982a85 --- /dev/null +++ b/src/giftCards/GiftCardSettings/types/GiftCardSettings.ts @@ -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; +} diff --git a/src/giftCards/GiftCardSettings/types/GiftCardSettingsUpdate.ts b/src/giftCards/GiftCardSettings/types/GiftCardSettingsUpdate.ts new file mode 100644 index 000000000..7b7ffd8f9 --- /dev/null +++ b/src/giftCards/GiftCardSettings/types/GiftCardSettingsUpdate.ts @@ -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; +} diff --git a/src/giftCards/GiftCardSettings/utils.ts b/src/giftCards/GiftCardSettings/utils.ts new file mode 100644 index 000000000..c58c3d54e --- /dev/null +++ b/src/giftCards/GiftCardSettings/utils.ts @@ -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 + }; +}; diff --git a/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/GiftCardResendCodeDialog.tsx b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/GiftCardResendCodeDialog.tsx new file mode 100644 index 000000000..87465b616 --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/GiftCardResendCodeDialog.tsx @@ -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 = ({ + 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 ( + + {loadingChannels ? ( +
+ +
+ ) : ( + <> + {intl.formatMessage(messages.description)} + + + + ) => + setConsentSelected(event.target.value) + } + /> + + + + )} +
+ ); +}; + +export default GiftCardResendCodeDialog; diff --git a/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/index.tsx b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/index.tsx new file mode 100644 index 000000000..9c98a7148 --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/index.tsx @@ -0,0 +1,2 @@ +export * from "./GiftCardResendCodeDialog"; +export { default } from "./GiftCardResendCodeDialog"; diff --git a/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/messages.ts b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/messages.ts new file mode 100644 index 000000000..b66adfcbd --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/messages.ts @@ -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" + } +}); diff --git a/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/mutations.ts b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/mutations.ts new file mode 100644 index 000000000..33ea2cabc --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/mutations.ts @@ -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); diff --git a/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/types/GiftCardResend.ts b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/types/GiftCardResend.ts new file mode 100644 index 000000000..cf96b7826 --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/types/GiftCardResend.ts @@ -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; +} diff --git a/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/utils.ts b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/utils.ts new file mode 100644 index 000000000..21d6c836c --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardResendCodeDialog/utils.ts @@ -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>(null); + + useEffect(() => { + if (!open) { + setFormErrors(null); + reset(); + } + }, [open]); + + useEffect(() => { + const errors = getFormErrors(keys, apiErrors); + setFormErrors(errors); + }, [apiErrors]); + + return { formErrors }; +} diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdateBalanceDialog/GiftCardUpdateBalanceDialog.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdateBalanceDialog/GiftCardUpdateBalanceDialog.tsx index 8a1326ea4..84ee314c3 100644 --- a/src/giftCards/GiftCardUpdate/GiftCardUpdateBalanceDialog/GiftCardUpdateBalanceDialog.tsx +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateBalanceDialog/GiftCardUpdateBalanceDialog.tsx @@ -1,16 +1,16 @@ import { TextField, Typography } from "@material-ui/core"; import ActionDialog from "@saleor/components/ActionDialog"; import CardSpacer from "@saleor/components/CardSpacer"; -import Form from "@saleor/components/Form"; import { IMessage } from "@saleor/components/messages"; +import useForm from "@saleor/hooks/useForm"; import useNotifier from "@saleor/hooks/useNotifier"; -import { getFormErrors } from "@saleor/utils/errors"; 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 { useIntl } from "react-intl"; import { giftCardsListTableMessages as tableMessages } from "../../GiftCardsList/messages"; +import { useDialogFormReset } from "../GiftCardResendCodeDialog/utils"; import { getGiftCardErrorMessage } from "../messages"; import { useGiftCardUpdateMutation } from "../mutations"; import useGiftCardDetails from "../providers/GiftCardDetailsProvider/hooks/useGiftCardDetails"; @@ -22,9 +22,9 @@ export interface GiftCardBalanceUpdateFormData { balanceAmount: number; } -const GiftCardUpdateBalanceDialog: React.FC = ({ +const GiftCardUpdateBalanceDialog: React.FC = ({ open, - onClose + closeDialog }) => { const intl = useIntl(); const classes = useStyles({}); @@ -57,7 +57,7 @@ const GiftCardUpdateBalanceDialog: React.FC = ({ notify(notifierData); if (!errors.length) { - onClose(); + closeDialog(); } }; @@ -83,54 +83,57 @@ const GiftCardUpdateBalanceDialog: React.FC = ({ return result?.data?.giftCardUpdate?.errors; }; - const { loading, status, data } = updateGiftCardBalanceOpts; - - const formErrors = getFormErrors( - ["initialBalanceAmount"], - data?.giftCardUpdate?.errors + const { data, change, submit, reset, hasChanged } = useForm( + initialFormData, + handleSubmit ); + const { loading, status, data: submitData } = updateGiftCardBalanceOpts; + + const { formErrors } = useDialogFormReset({ + open, + reset, + keys: ["initialBalanceAmount"], + apiErrors: submitData?.giftCardUpdate?.errors + }); + return ( -
- {({ data, change, submit, hasChanged }) => ( - - {intl.formatMessage(messages.subtitle)} - - - {currency} -
- ) - }} - /> - - )} - + + {intl.formatMessage(messages.subtitle)} + + + {currency} + + ) + }} + /> + ); }; diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdateDetailsCard/GiftCardUpdateDetailsCard.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdateDetailsCard/GiftCardUpdateDetailsCard.tsx index 2c7e24276..25414a417 100644 --- a/src/giftCards/GiftCardUpdate/GiftCardUpdateDetailsCard/GiftCardUpdateDetailsCard.tsx +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateDetailsCard/GiftCardUpdateDetailsCard.tsx @@ -2,8 +2,8 @@ import { Button, Card, CardContent, Divider } from "@material-ui/core"; import CardSpacer from "@saleor/components/CardSpacer"; import CardTitle from "@saleor/components/CardTitle"; import Skeleton from "@saleor/components/Skeleton"; -import GiftCardExpirySelect from "@saleor/giftCards/components/GiftCardExpirySelect"; import GiftCardTagInput from "@saleor/giftCards/components/GiftCardTagInput"; +import GiftCardUpdateExpirySelect from "@saleor/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect"; import React from "react"; import { useIntl } from "react-intl"; @@ -21,7 +21,7 @@ const GiftCardUpdateDetailsCard: React.FC = () => { const { change, - data: { expiryType, expiryPeriodAmount, expiryPeriodType, tag, expiryDate }, + data: { tag }, formErrors } = useGiftCardUpdateForm(); @@ -55,14 +55,7 @@ const GiftCardUpdateDetailsCard: React.FC = () => { change={change} /> - + )} diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/GiftCardUpdateExpirySelect.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/GiftCardUpdateExpirySelect.tsx new file mode 100644 index 000000000..2d9d94fcd --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/GiftCardUpdateExpirySelect.tsx @@ -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 ( + <> + {intl.formatMessage(messages.expiryDateLabel)} + + setCardExpiresSelected(event.target.value)} + /> + + + {cardExpiresSelected && ( + + )} + + ); +}; + +export default GiftCardUpdateExpirySelect; diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/index.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/index.tsx new file mode 100644 index 000000000..7fbc63094 --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/index.tsx @@ -0,0 +1,2 @@ +export * from "./GiftCardUpdateExpirySelect"; +export { default } from "./GiftCardUpdateExpirySelect"; diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/messages.ts b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/messages.ts new file mode 100644 index 000000000..b684596a4 --- /dev/null +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/messages.ts @@ -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" + } +}); diff --git a/src/giftCards/components/GiftCardExpirySelect/styles.ts b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/styles.ts similarity index 70% rename from src/giftCards/components/GiftCardExpirySelect/styles.ts rename to src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/styles.ts index 841694856..81ade85cd 100644 --- a/src/giftCards/components/GiftCardExpirySelect/styles.ts +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect/styles.ts @@ -1,13 +1,19 @@ import { makeStyles } from "@saleor/macaw-ui"; export const useGiftCardExpirySelectStyles = makeStyles( - () => ({ + theme => ({ radioGroupContainer: { display: "flex", flexDirection: "row" }, dateField: { width: 400 + }, + periodField: { + display: "flex" + }, + dateText: { + marginTop: theme.spacing(0.5) } }), { name: "GiftCardUpdateDetailsExpirySection" } diff --git a/src/giftCards/GiftCardUpdate/GiftCardUpdateInfoCard/GiftCardUpdateInfoCardContent.tsx b/src/giftCards/GiftCardUpdate/GiftCardUpdateInfoCard/GiftCardUpdateInfoCardContent.tsx index 57caab8fc..ed82d52d6 100644 --- a/src/giftCards/GiftCardUpdate/GiftCardUpdateInfoCard/GiftCardUpdateInfoCardContent.tsx +++ b/src/giftCards/GiftCardUpdate/GiftCardUpdateInfoCard/GiftCardUpdateInfoCardContent.tsx @@ -8,6 +8,7 @@ import useNavigator from "@saleor/hooks/useNavigator"; import { getFullName, getStringOrPlaceholder } from "@saleor/misc"; import Label from "@saleor/orders/components/OrderHistory/Label"; import { getOrderNumberLinkObject } from "@saleor/orders/components/OrderHistory/utils"; +import { getByType } from "@saleor/orders/components/OrderReturnPage/utils"; import { productUrl } from "@saleor/products/urls"; import { staffMemberDetailsUrl } from "@saleor/staff/urls"; import { GiftCardEventsEnum } from "@saleor/types/globalTypes"; @@ -37,9 +38,8 @@ const GiftCardUpdateInfoCardContent: React.FC = () => { events } = giftCard; - const cardIssuedEvent = events.find( - ({ type }) => type === GiftCardEventsEnum.ISSUED - ); + const cardIssuedEvent = events.find(getByType(GiftCardEventsEnum.ISSUED)); + const cardBoughtEvent = events.find(getByType(GiftCardEventsEnum.BOUGHT)); const getBuyerFieldData = (): { label: MessageDescriptor; @@ -80,13 +80,31 @@ const GiftCardUpdateInfoCardContent: React.FC = () => { }; }; - const orderData = - cardIssuedEvent && cardIssuedEvent.orderId - ? getOrderNumberLinkObject({ - id: cardIssuedEvent.orderId, - number: cardIssuedEvent.orderNumber - }) - : null; + const getOrderData = () => { + if (cardIssuedEvent) { + const { orderId, orderNumber } = cardIssuedEvent; + + if (!orderId) { + return null; + } + + return getOrderNumberLinkObject({ + id: orderId, + number: orderNumber + }); + } + + if (cardBoughtEvent) { + const { orderId, orderNumber } = cardBoughtEvent; + + return getOrderNumberLinkObject({ + id: orderId, + number: orderNumber + }); + } + + return null; + }; const { label: buyerLabelMessage, @@ -94,6 +112,8 @@ const GiftCardUpdateInfoCardContent: React.FC = () => { url: buyerUrl } = getBuyerFieldData(); + const orderData = getOrderData(); + return ( <>