@faststore/core 4.8.1-dev.0 → 4.8.1-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-generate.log +3 -3
- package/.turbo/turbo-test.log +146 -105
- package/@generated/gql.ts +3 -3
- package/@generated/graphql.ts +4 -2
- package/@generated/persisted-documents.json +1 -1
- package/@generated/schema.graphql +4 -0
- package/CHANGELOG.md +16 -0
- package/cms/faststore/my-account/components/cms_component__accountnavigation.jsonc +10 -0
- package/cms/faststore/my-account/components/cms_component__accountorderdelivery.jsonc +80 -0
- package/cms/faststore/my-account/components/cms_component__accountorderdetails.jsonc +25 -0
- package/cms/faststore/my-account/components/cms_component__accountorderslist.jsonc +10 -0
- package/cms/faststore/my-account/components/cms_component__accountorderstatus.jsonc +90 -0
- package/cms/faststore/my-account/components/cms_component__accountordersummary.jsonc +25 -0
- package/package.json +6 -6
- package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +4 -6
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +18 -3
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerFooter.tsx +13 -2
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +1 -0
- package/src/components/account/Drawer/OrganizationDrawer/section.module.scss +7 -0
- package/src/components/account/Drawer/OrganizationSignInButton/OrganizationSignInButton.tsx +7 -9
- package/src/components/account/Menu/Menu.tsx +24 -7
- package/src/components/account/orders/ListOrders/FilterSlider/FilterFacetPendingApproval/FilterFacetPendingApproval.tsx +8 -3
- package/src/components/account/orders/ListOrders/FilterSlider/FilterSlider.tsx +1 -0
- package/src/components/account/orders/ListOrders/ListOrders.tsx +2 -2
- package/src/components/account/orders/ListOrders/listOrdersLabels.ts +4 -0
- package/src/components/account/orders/OrderDetails/BuyingPolicyAlert/BuyingPolicyAlert.tsx +45 -9
- package/src/components/account/orders/OrderDetails/DeliveryCard/DeliveryCard.tsx +14 -3
- package/src/components/account/orders/OrderDetails/DeliveryOptionAccordion/DeliveryOptionAccordion.tsx +4 -1
- package/src/components/account/orders/OrderDetails/DeliveryOptionAccordion/DeliveryOptionAccordionDeliveryInfo.tsx +14 -7
- package/src/components/account/orders/OrderDetails/DeliveryOptionAccordion/DeliveryOptionAccordionProducts.tsx +8 -5
- package/src/components/account/orders/OrderDetails/OrderDetailsHeader.tsx +5 -0
- package/src/components/account/orders/OrderDetails/StatusCard/StatusCard.tsx +26 -89
- package/src/components/account/orders/OrderDetails/StatusCard/statusCardLocalization.ts +114 -0
- package/src/components/account/orders/OrderDetails/SummaryCard/SummaryCard.tsx +2 -1
- package/src/components/account/orders/OrderDetails/SummaryCard/getOrderTotalLabel.ts +19 -0
- package/src/components/account/orders/OrderDetails/getDeliveryOptionLabel.ts +135 -0
- package/src/components/account/orders/OrderDetails/orderDetailsLabels.ts +73 -0
- package/src/components/navigation/Navbar/Navbar.tsx +17 -19
- package/src/components/navigation/Navbar/NavbarAccountArea.tsx +57 -0
- package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +16 -18
- package/src/components/sections/Account/AccountOrderDelivery.tsx +3 -1
- package/src/components/sections/Account/AccountOrderDetails.tsx +16 -0
- package/src/components/sections/Account/AccountOrderStatus.tsx +27 -3
- package/src/components/sections/Account/AccountOrdersList.tsx +4 -0
- package/src/pages/api/fs/switch-contract.ts +194 -0
- package/src/pages/api/graphql.ts +108 -69
- package/src/sdk/account/accountPageContext.ts +14 -3
- package/src/sdk/account/changeContractToken.ts +19 -63
- package/src/sdk/account/clearPersistedSessionState.ts +13 -7
- package/src/sdk/account/getMyAccountRoutes.ts +2 -0
- package/src/sdk/account/useAvailableContracts.ts +1 -0
- package/src/sdk/account/useSwitchContract.ts +11 -3
- package/src/sdk/overrides/PageProvider.tsx +1 -1
- package/src/sdk/session/index.ts +2 -1
- package/src/sdk/session/isSignInAreaResolved.ts +12 -0
- package/test/components/account/ContractSwitcher.browser.test.tsx +42 -0
- package/test/components/account/Menu.browser.test.tsx +123 -0
- package/test/components/account/OrganizationDrawer.browser.test.tsx +148 -0
- package/test/components/account/OrganizationDrawerFooter.browser.test.tsx +95 -1
- package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +2 -0
- package/test/components/account/OrganizationSignInButton.browser.test.tsx +49 -0
- package/test/components/account/orders/BuyingPolicyAlert.browser.test.tsx +342 -0
- package/test/components/account/orders/DeliveryCard.browser.test.tsx +36 -0
- package/test/components/account/orders/DeliveryOptionAccordion.browser.test.tsx +76 -0
- package/test/components/account/orders/DeliveryOptionAccordionDeliveryInfo.browser.test.tsx +41 -0
- package/test/components/account/orders/DeliveryOptionAccordionProducts.browser.test.tsx +35 -0
- package/test/components/account/orders/FilterFacetPendingApproval.browser.test.tsx +25 -0
- package/test/components/account/orders/StatusCard.browser.test.tsx +37 -0
- package/test/components/account/orders/StatusCard.test.ts +101 -0
- package/test/components/account/orders/SummaryCard.browser.test.tsx +59 -0
- package/test/components/account/orders/getDeliveryOptionLabel.test.ts +129 -0
- package/test/components/account/orders/getOrderTotalLabel.test.ts +35 -0
- package/test/components/account/orders/listOrdersLabels.test.ts +23 -0
- package/test/components/account/orders/orderDetailsLabels.test.ts +13 -0
- package/test/components/navigation/Navbar.browser.test.tsx +183 -0
- package/test/components/navigation/NavbarAccountArea.browser.test.tsx +115 -0
- package/test/components/navigation/NavbarSlider.browser.test.tsx +143 -0
- package/test/components/sections/Account/AccountOrderDelivery.browser.test.tsx +72 -0
- package/test/components/sections/Account/AccountOrderDetails.browser.test.tsx +151 -0
- package/test/components/sections/Account/AccountOrderStatus.browser.test.tsx +59 -0
- package/test/components/sections/Account/AccountOrdersList.browser.test.tsx +67 -0
- package/test/pages/api/graphql.test.ts +27 -1
- package/test/pages/api/switch-contract.test.ts +385 -0
- package/test/sdk/account/changeContractToken.browser.test.ts +23 -71
- package/test/sdk/account/clearPersistedSessionState.browser.test.ts +24 -0
- package/test/sdk/account/getMyAccountRoutes.test.ts +11 -1
- package/test/sdk/account/useSwitchContract.browser.test.ts +21 -0
- package/test/sdk/session/isSignInAreaResolved.test.ts +22 -0
- package/test/utils/userOrderStatus.test.ts +22 -0
|
@@ -18,6 +18,13 @@ interface BuyingPolicyAlertProps {
|
|
|
18
18
|
rejectModalTitle?: string
|
|
19
19
|
rejectModalMessage?: string
|
|
20
20
|
rejectModalConfirmText?: string
|
|
21
|
+
/** Supports the `{policy}` placeholder, replaced with the policy name. */
|
|
22
|
+
approveSuccessToast?: string
|
|
23
|
+
approveErrorToast?: string
|
|
24
|
+
/** Supports the `{policy}` placeholder, replaced with the policy name. */
|
|
25
|
+
rejectSuccessToast?: string
|
|
26
|
+
rejectErrorToast?: string
|
|
27
|
+
pendingFurtherApprovalsAlert?: string
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
|
|
@@ -28,9 +35,22 @@ const DEFAULT_REJECT_MODAL_TITLE = 'Reject approval request'
|
|
|
28
35
|
const DEFAULT_REJECT_MODAL_MESSAGE =
|
|
29
36
|
"You're about to reject this approval request, triggered by the {policy} policy. Rejecting any approval request will deny the entire order.\n\nThis action is permanent and cannot be undone."
|
|
30
37
|
const DEFAULT_REJECT_MODAL_CONFIRM_TEXT = 'Reject'
|
|
38
|
+
const DEFAULT_APPROVE_SUCCESS_TOAST = '{policy} policy approved successfully.'
|
|
39
|
+
const DEFAULT_APPROVE_ERROR_TOAST =
|
|
40
|
+
"Policy couldn't be approved due to a technical issue."
|
|
41
|
+
const DEFAULT_REJECT_SUCCESS_TOAST =
|
|
42
|
+
'{policy} policy rejected successfully. Order denied.'
|
|
43
|
+
const DEFAULT_REJECT_ERROR_TOAST =
|
|
44
|
+
"Policy couldn't be rejected due to a technical issue."
|
|
45
|
+
const DEFAULT_PENDING_FURTHER_APPROVALS_ALERT =
|
|
46
|
+
'Your approval is recorded. This order is still pending further approvals.'
|
|
47
|
+
|
|
48
|
+
function interpolatePolicyName(template: string, policyName: string): string {
|
|
49
|
+
return template.split('{policy}').join(policyName)
|
|
50
|
+
}
|
|
31
51
|
|
|
32
52
|
function renderRejectMessage(template: string, policyName: string): ReactNode {
|
|
33
|
-
const lines = template
|
|
53
|
+
const lines = interpolatePolicyName(template, policyName).split('\n')
|
|
34
54
|
|
|
35
55
|
return lines.map((line, index) => (
|
|
36
56
|
<Fragment key={`${index}-${line}`}>
|
|
@@ -53,6 +73,17 @@ export default function BuyingPolicyAlert({
|
|
|
53
73
|
labels?.rejectModalMessage ?? DEFAULT_REJECT_MODAL_MESSAGE
|
|
54
74
|
const rejectModalConfirmText =
|
|
55
75
|
labels?.rejectModalConfirmText ?? DEFAULT_REJECT_MODAL_CONFIRM_TEXT
|
|
76
|
+
const approveSuccessToast =
|
|
77
|
+
labels?.approveSuccessToast ?? DEFAULT_APPROVE_SUCCESS_TOAST
|
|
78
|
+
const approveErrorToast =
|
|
79
|
+
labels?.approveErrorToast ?? DEFAULT_APPROVE_ERROR_TOAST
|
|
80
|
+
const rejectSuccessToast =
|
|
81
|
+
labels?.rejectSuccessToast ?? DEFAULT_REJECT_SUCCESS_TOAST
|
|
82
|
+
const rejectErrorToast =
|
|
83
|
+
labels?.rejectErrorToast ?? DEFAULT_REJECT_ERROR_TOAST
|
|
84
|
+
const pendingFurtherApprovalsAlert =
|
|
85
|
+
labels?.pendingFurtherApprovalsAlert ??
|
|
86
|
+
DEFAULT_PENDING_FURTHER_APPROVALS_ALERT
|
|
56
87
|
const { pushToast } = useUI()
|
|
57
88
|
const [isAuthorizationOpen, setIsAuthorizationOpen] = useState<boolean>(false)
|
|
58
89
|
const { data, error, processOrderAuthorization, loading } =
|
|
@@ -72,15 +103,18 @@ export default function BuyingPolicyAlert({
|
|
|
72
103
|
// Success toast
|
|
73
104
|
pushToast({
|
|
74
105
|
status: 'INFO',
|
|
75
|
-
message:
|
|
106
|
+
message: interpolatePolicyName(
|
|
107
|
+
approveSuccessToast,
|
|
108
|
+
ruleForAuthorization.rule.name
|
|
109
|
+
),
|
|
76
110
|
icon: <UIIcon width={30} height={30} name="CircleWavyCheck" />,
|
|
77
111
|
})
|
|
78
112
|
|
|
79
113
|
onAuthorizationComplete?.()
|
|
80
|
-
} catch
|
|
114
|
+
} catch {
|
|
81
115
|
pushToast({
|
|
82
116
|
status: 'ERROR',
|
|
83
|
-
message:
|
|
117
|
+
message: approveErrorToast,
|
|
84
118
|
icon: <UIIcon width={30} height={30} name="CircleWavyWarning" />,
|
|
85
119
|
})
|
|
86
120
|
}
|
|
@@ -99,16 +133,19 @@ export default function BuyingPolicyAlert({
|
|
|
99
133
|
|
|
100
134
|
pushToast({
|
|
101
135
|
status: 'INFO',
|
|
102
|
-
message:
|
|
136
|
+
message: interpolatePolicyName(
|
|
137
|
+
rejectSuccessToast,
|
|
138
|
+
ruleForAuthorization.rule.name
|
|
139
|
+
),
|
|
103
140
|
icon: <UIIcon width={30} height={30} name="XCircle" />,
|
|
104
141
|
})
|
|
105
142
|
|
|
106
143
|
setIsAuthorizationOpen(false)
|
|
107
144
|
onAuthorizationComplete?.()
|
|
108
|
-
} catch
|
|
145
|
+
} catch {
|
|
109
146
|
pushToast({
|
|
110
147
|
status: 'ERROR',
|
|
111
|
-
message:
|
|
148
|
+
message: rejectErrorToast,
|
|
112
149
|
icon: <UIIcon width={30} height={30} name="CircleWavyWarning" />,
|
|
113
150
|
})
|
|
114
151
|
}
|
|
@@ -159,8 +196,7 @@ export default function BuyingPolicyAlert({
|
|
|
159
196
|
data-fs-pending-policies-alert
|
|
160
197
|
icon={<UIIcon name="Info" width={20} height={20} />}
|
|
161
198
|
>
|
|
162
|
-
|
|
163
|
-
approvals.
|
|
199
|
+
{pendingFurtherApprovalsAlert}
|
|
164
200
|
</UIAlert>
|
|
165
201
|
)}
|
|
166
202
|
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { UserOrderDeliveryOptionsData } from '@generated/graphql'
|
|
2
2
|
import { camelCaseToTitle } from 'src/utils/utilities'
|
|
3
3
|
import Card from '../../../components/Card'
|
|
4
|
+
import {
|
|
5
|
+
getDeliveryOptionKey,
|
|
6
|
+
getDeliveryOptionLabel,
|
|
7
|
+
} from '../getDeliveryOptionLabel'
|
|
8
|
+
import {
|
|
9
|
+
type OrderDeliverySectionLabels,
|
|
10
|
+
resolveOrderDeliveryLabels,
|
|
11
|
+
} from '../orderDetailsLabels'
|
|
4
12
|
|
|
5
13
|
interface DeliveryCardProps {
|
|
6
14
|
title?: string
|
|
7
15
|
deliveryOptionsData?: UserOrderDeliveryOptionsData
|
|
16
|
+
labels?: OrderDeliverySectionLabels
|
|
8
17
|
fields?: Array<{
|
|
9
18
|
name: string
|
|
10
19
|
value: string
|
|
@@ -15,8 +24,10 @@ interface DeliveryCardProps {
|
|
|
15
24
|
export default function DeliveryCard({
|
|
16
25
|
title = 'Delivery',
|
|
17
26
|
deliveryOptionsData,
|
|
27
|
+
labels: labelsProp,
|
|
18
28
|
fields,
|
|
19
|
-
}: DeliveryCardProps) {
|
|
29
|
+
}: Readonly<DeliveryCardProps>) {
|
|
30
|
+
const labels = resolveOrderDeliveryLabels(labelsProp)
|
|
20
31
|
const deliveryOptions = deliveryOptionsData?.deliveryOptions ?? []
|
|
21
32
|
const contact = deliveryOptionsData?.contact
|
|
22
33
|
|
|
@@ -24,8 +35,8 @@ export default function DeliveryCard({
|
|
|
24
35
|
<Card title={title} data-fs-order-delivery-card>
|
|
25
36
|
<div data-fs-delivery-methods>
|
|
26
37
|
{deliveryOptions.map((option) => (
|
|
27
|
-
<p key={option
|
|
28
|
-
{option
|
|
38
|
+
<p key={getDeliveryOptionKey(option)} data-fs-delivery-method>
|
|
39
|
+
{getDeliveryOptionLabel(option, labels)}
|
|
29
40
|
</p>
|
|
30
41
|
))}
|
|
31
42
|
</div>
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type OrderDeliverySectionLabels,
|
|
21
21
|
resolveOrderDeliveryLabels,
|
|
22
22
|
} from '../orderDetailsLabels'
|
|
23
|
+
import { getDeliveryOptionLabel } from '../getDeliveryOptionLabel'
|
|
23
24
|
|
|
24
25
|
interface DeliveryOptionAccordionProps {
|
|
25
26
|
deliveryOption: UserOrderDeliveryOption
|
|
@@ -50,7 +51,7 @@ function DeliveryOptionAccordion({
|
|
|
50
51
|
)
|
|
51
52
|
const formatPrice = useFormatPrice()
|
|
52
53
|
|
|
53
|
-
const title = deliveryOption
|
|
54
|
+
const title = getDeliveryOptionLabel(deliveryOption, labels)
|
|
54
55
|
|
|
55
56
|
const itemCount = labels.itemCountTemplate.replace(
|
|
56
57
|
'{count}',
|
|
@@ -80,6 +81,7 @@ function DeliveryOptionAccordion({
|
|
|
80
81
|
<DeliveryOptionAccordionDeliveryInfo
|
|
81
82
|
deliveryOption={deliveryOption}
|
|
82
83
|
contact={contact}
|
|
84
|
+
labels={labels}
|
|
83
85
|
/>
|
|
84
86
|
<DeliveryOptionAccordionProducts>
|
|
85
87
|
{deliveryOption.items?.map((item, index) => {
|
|
@@ -97,6 +99,7 @@ function DeliveryOptionAccordion({
|
|
|
97
99
|
price={formatPrice(item.sellingPrice ?? 0, currencyCode)}
|
|
98
100
|
tax={formatPrice(tax, currencyCode)}
|
|
99
101
|
total={formatPrice(item.total ?? 0, currencyCode)}
|
|
102
|
+
labels={labels}
|
|
100
103
|
/>
|
|
101
104
|
)
|
|
102
105
|
})}
|
|
@@ -3,13 +3,14 @@ import type {
|
|
|
3
3
|
UserOrderDeliveryOption,
|
|
4
4
|
UserOrderDeliveryOptionsContact,
|
|
5
5
|
} from '@generated/graphql'
|
|
6
|
+
import type { OrderDeliverySectionLabels } from '../orderDetailsLabels'
|
|
6
7
|
|
|
7
8
|
interface InfoContainerProps {
|
|
8
9
|
title: string
|
|
9
10
|
children: ReactNode
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
function InfoContainer({ title, children }: InfoContainerProps) {
|
|
13
|
+
function InfoContainer({ title, children }: Readonly<InfoContainerProps>) {
|
|
13
14
|
return (
|
|
14
15
|
<div data-fs-delivery-option-accordion-info-container>
|
|
15
16
|
<span data-fs-delivery-option-accordion-info-title>{title}</span>
|
|
@@ -21,12 +22,14 @@ function InfoContainer({ title, children }: InfoContainerProps) {
|
|
|
21
22
|
interface DeliveryOptionAccordionDeliveryInfoProps {
|
|
22
23
|
deliveryOption: UserOrderDeliveryOption
|
|
23
24
|
contact?: UserOrderDeliveryOptionsContact | null
|
|
25
|
+
labels: Required<OrderDeliverySectionLabels>
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
function DeliveryOptionAccordionDeliveryInfo({
|
|
27
29
|
deliveryOption,
|
|
28
30
|
contact,
|
|
29
|
-
|
|
31
|
+
labels,
|
|
32
|
+
}: Readonly<DeliveryOptionAccordionDeliveryInfoProps>) {
|
|
30
33
|
const isPickup = deliveryOption.deliveryChannel === 'pickup-in-point'
|
|
31
34
|
const address = isPickup
|
|
32
35
|
? deliveryOption.pickupStoreInfo?.address
|
|
@@ -39,7 +42,7 @@ function DeliveryOptionAccordionDeliveryInfo({
|
|
|
39
42
|
return (
|
|
40
43
|
<div data-fs-delivery-option-accordion-delivery-info>
|
|
41
44
|
{!isPickup && contact && (
|
|
42
|
-
<InfoContainer title=
|
|
45
|
+
<InfoContainer title={labels.recipientLabel}>
|
|
43
46
|
<span data-fs-delivery-option-accordion-info>
|
|
44
47
|
<strong>{contact.name}</strong>
|
|
45
48
|
</span>
|
|
@@ -52,7 +55,11 @@ function DeliveryOptionAccordionDeliveryInfo({
|
|
|
52
55
|
</InfoContainer>
|
|
53
56
|
)}
|
|
54
57
|
|
|
55
|
-
<InfoContainer
|
|
58
|
+
<InfoContainer
|
|
59
|
+
title={
|
|
60
|
+
isPickup ? labels.storeAddressLabel : labels.deliveryAddressLabel
|
|
61
|
+
}
|
|
62
|
+
>
|
|
56
63
|
<span data-fs-delivery-option-accordion-info>
|
|
57
64
|
<strong>{address.city}</strong>
|
|
58
65
|
</span>
|
|
@@ -64,15 +71,15 @@ function DeliveryOptionAccordionDeliveryInfo({
|
|
|
64
71
|
|
|
65
72
|
{/* TODO: Validate this after we check the return from api */}
|
|
66
73
|
{isPickup && deliveryOption.deliveryWindow && (
|
|
67
|
-
<InfoContainer title=
|
|
74
|
+
<InfoContainer title={labels.storeHoursLabel}>
|
|
68
75
|
<span data-fs-delivery-option-accordion-info>
|
|
69
|
-
|
|
76
|
+
{labels.fromLabel}{' '}
|
|
70
77
|
{new Date(
|
|
71
78
|
deliveryOption.deliveryWindow.startDateUtc
|
|
72
79
|
).toLocaleString()}
|
|
73
80
|
</span>
|
|
74
81
|
<span data-fs-delivery-option-accordion-info>
|
|
75
|
-
|
|
82
|
+
{labels.toLabel}{' '}
|
|
76
83
|
{new Date(
|
|
77
84
|
deliveryOption.deliveryWindow.endDateUtc
|
|
78
85
|
).toLocaleString()}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
import { camelCaseToTitle } from 'src/utils/utilities'
|
|
3
|
+
import type { OrderDeliverySectionLabels } from '../orderDetailsLabels'
|
|
3
4
|
|
|
4
5
|
interface DeliveryOptionAccordionProductProps {
|
|
5
6
|
image: string
|
|
@@ -13,6 +14,7 @@ interface DeliveryOptionAccordionProductProps {
|
|
|
13
14
|
price: string
|
|
14
15
|
tax: string
|
|
15
16
|
total: string
|
|
17
|
+
labels: Required<OrderDeliverySectionLabels>
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
function DeliveryOptionAccordionProduct({
|
|
@@ -23,7 +25,8 @@ function DeliveryOptionAccordionProduct({
|
|
|
23
25
|
price,
|
|
24
26
|
tax,
|
|
25
27
|
total,
|
|
26
|
-
|
|
28
|
+
labels,
|
|
29
|
+
}: Readonly<DeliveryOptionAccordionProductProps>) {
|
|
27
30
|
return (
|
|
28
31
|
<div data-fs-delivery-option-accordion-product>
|
|
29
32
|
<div data-fs-delivery-option-accordion-product-top>
|
|
@@ -43,15 +46,15 @@ function DeliveryOptionAccordionProduct({
|
|
|
43
46
|
<div data-fs-delivery-option-accordion-product-bottom>
|
|
44
47
|
<div data-fs-delivery-option-accordion-product-price>
|
|
45
48
|
<p>{price}</p>
|
|
46
|
-
<span>
|
|
49
|
+
<span>{labels.eachLabel}</span>
|
|
47
50
|
</div>
|
|
48
51
|
<div data-fs-delivery-option-accordion-product-tax>
|
|
49
52
|
<p>{tax}</p>
|
|
50
|
-
<span>
|
|
53
|
+
<span>{labels.taxesLabel}</span>
|
|
51
54
|
</div>
|
|
52
55
|
<div data-fs-delivery-option-accordion-product-total>
|
|
53
56
|
<p>{total}</p>
|
|
54
|
-
<span>
|
|
57
|
+
<span>{labels.totalLabel}</span>
|
|
55
58
|
</div>
|
|
56
59
|
</div>
|
|
57
60
|
</div>
|
|
@@ -64,7 +67,7 @@ interface DeliveryOptionAccordionProductsProps {
|
|
|
64
67
|
|
|
65
68
|
function DeliveryOptionAccordionProducts({
|
|
66
69
|
children,
|
|
67
|
-
}: DeliveryOptionAccordionProductsProps) {
|
|
70
|
+
}: Readonly<DeliveryOptionAccordionProductsProps>) {
|
|
68
71
|
return <div data-fs-delivery-option-accordion-products>{children}</div>
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -78,6 +78,11 @@ export function OrderDetailsHeader({
|
|
|
78
78
|
rejectModalTitle: labels.rejectModalTitle,
|
|
79
79
|
rejectModalMessage: labels.rejectModalMessage,
|
|
80
80
|
rejectModalConfirmText: labels.rejectModalConfirmText,
|
|
81
|
+
approveSuccessToast: labels.approveSuccessToast,
|
|
82
|
+
approveErrorToast: labels.approveErrorToast,
|
|
83
|
+
rejectSuccessToast: labels.rejectSuccessToast,
|
|
84
|
+
rejectErrorToast: labels.rejectErrorToast,
|
|
85
|
+
pendingFurtherApprovalsAlert: labels.pendingFurtherApprovalsAlert,
|
|
81
86
|
}}
|
|
82
87
|
/>
|
|
83
88
|
)}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { Icon as UIIcon, Skeleton as UISkeleton } from '@faststore/ui'
|
|
2
|
+
import { useRouter } from 'next/router'
|
|
2
3
|
import { useRef, type ReactNode } from 'react'
|
|
3
4
|
import Card from 'src/components/account/components/Card'
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
orderStatusMap,
|
|
7
|
+
type OrderStatusCmsLabels,
|
|
8
|
+
type OrderStatusKey,
|
|
9
|
+
} from 'src/utils/userOrderStatus'
|
|
5
10
|
import { useConnectorPositioning } from './useConnectorPositioning'
|
|
6
11
|
import type { ServerOrderDetailsQueryQuery } from '@generated/graphql'
|
|
7
12
|
import {
|
|
8
13
|
type OrderStatusSectionLabels,
|
|
9
14
|
resolveOrderStatusLabels,
|
|
10
15
|
} from '../orderDetailsLabels'
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| 'processing'
|
|
18
|
-
| 'shipping'
|
|
16
|
+
import {
|
|
17
|
+
formatDate,
|
|
18
|
+
getDisplayStepLabel,
|
|
19
|
+
type StepKey,
|
|
20
|
+
type StepStatus,
|
|
21
|
+
} from './statusCardLocalization'
|
|
19
22
|
|
|
20
23
|
interface Step {
|
|
21
24
|
label: ReactNode
|
|
@@ -27,63 +30,24 @@ interface StatusCardProps {
|
|
|
27
30
|
status: OrderStatusKey
|
|
28
31
|
creationDate: ServerOrderDetailsQueryQuery['userOrder']['creationDate']
|
|
29
32
|
labels?: OrderStatusSectionLabels
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Define custom labels for each step based on their status
|
|
33
|
-
// This allows us to show different text for the same step depending on its current state
|
|
34
|
-
const STEP_LABELS: Record<StepKey, Record<StepStatus, string>> = {
|
|
35
|
-
order: {
|
|
36
|
-
completed: 'Order Placed',
|
|
37
|
-
loading: 'Order Placed',
|
|
38
|
-
'not-started': 'Order Placed',
|
|
39
|
-
failed: 'Order Placed',
|
|
40
|
-
},
|
|
41
|
-
approval: {
|
|
42
|
-
completed: 'Approved', // Custom
|
|
43
|
-
loading: 'Pending approval', // Use from orderStatusMap
|
|
44
|
-
'not-started': 'Pending approval',
|
|
45
|
-
failed: 'Denied', // Custom
|
|
46
|
-
},
|
|
47
|
-
payment: {
|
|
48
|
-
completed: 'Payment Approved', // Use from orderStatusMap
|
|
49
|
-
loading: 'Payment Pending', // Use from orderStatusMap
|
|
50
|
-
'not-started': 'Payment authorization', // Custom
|
|
51
|
-
failed: 'Payment Denied', // Use from orderStatusMap
|
|
52
|
-
},
|
|
53
|
-
processing: {
|
|
54
|
-
completed: 'Ready for Delivery', // Use from orderStatusMap
|
|
55
|
-
loading: 'Handling order', // Custom
|
|
56
|
-
'not-started': 'Handling order', // Custom
|
|
57
|
-
failed: 'Canceled', // Use from orderStatusMap
|
|
58
|
-
},
|
|
59
|
-
shipping: {
|
|
60
|
-
completed: 'Invoiced', // Use from orderStatusMap
|
|
61
|
-
loading: 'Shipping order', // Custom
|
|
62
|
-
'not-started': 'Ship order', // Custom
|
|
63
|
-
failed: 'Canceled', // Use from orderStatusMap
|
|
64
|
-
},
|
|
33
|
+
statusLabels?: OrderStatusCmsLabels
|
|
65
34
|
}
|
|
66
35
|
|
|
67
36
|
// Define the visual progression of order steps from start to finish
|
|
68
37
|
const VISUAL_STEPS = [
|
|
69
38
|
{
|
|
70
|
-
label: (stepStatus: StepStatus) => STEP_LABELS.order[stepStatus],
|
|
71
39
|
key: 'order',
|
|
72
40
|
},
|
|
73
41
|
{
|
|
74
|
-
label: (stepStatus: StepStatus) => STEP_LABELS.approval[stepStatus],
|
|
75
42
|
key: 'approval',
|
|
76
43
|
},
|
|
77
44
|
{
|
|
78
|
-
label: (stepStatus: StepStatus) => STEP_LABELS.payment[stepStatus],
|
|
79
45
|
key: 'payment',
|
|
80
46
|
},
|
|
81
47
|
{
|
|
82
|
-
label: (stepStatus: StepStatus) => STEP_LABELS.processing[stepStatus],
|
|
83
48
|
key: 'processing',
|
|
84
49
|
},
|
|
85
50
|
{
|
|
86
|
-
label: (stepStatus: StepStatus) => STEP_LABELS.shipping[stepStatus],
|
|
87
51
|
key: 'shipping',
|
|
88
52
|
},
|
|
89
53
|
] as const
|
|
@@ -105,24 +69,6 @@ const CANCELED_LABELS = ['Cancellation Requested', 'Canceled']
|
|
|
105
69
|
// Define which order status labels indicate a failed state
|
|
106
70
|
const FAILED_LABELS = ['Payment Denied']
|
|
107
71
|
|
|
108
|
-
const formatDate = (date: string) => {
|
|
109
|
-
const dateObj = new Date(date)
|
|
110
|
-
|
|
111
|
-
const dateString = new Intl.DateTimeFormat('en-US', {
|
|
112
|
-
month: '2-digit',
|
|
113
|
-
day: '2-digit',
|
|
114
|
-
year: 'numeric',
|
|
115
|
-
}).format(dateObj)
|
|
116
|
-
|
|
117
|
-
const timeString = new Intl.DateTimeFormat('en-US', {
|
|
118
|
-
hour: 'numeric',
|
|
119
|
-
minute: '2-digit',
|
|
120
|
-
hour12: true,
|
|
121
|
-
}).format(dateObj)
|
|
122
|
-
|
|
123
|
-
return { date: dateString, time: timeString }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
72
|
const StepIcon = ({
|
|
127
73
|
status,
|
|
128
74
|
isCanceled,
|
|
@@ -221,8 +167,10 @@ function StatusCard({
|
|
|
221
167
|
status,
|
|
222
168
|
creationDate,
|
|
223
169
|
labels: labelsProp,
|
|
224
|
-
|
|
170
|
+
statusLabels,
|
|
171
|
+
}: Readonly<StatusCardProps>) {
|
|
225
172
|
const labels = resolveOrderStatusLabels(labelsProp)
|
|
173
|
+
const { locale = 'en-US' } = useRouter()
|
|
226
174
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
227
175
|
|
|
228
176
|
useConnectorPositioning(containerRef)
|
|
@@ -244,25 +192,14 @@ function StatusCard({
|
|
|
244
192
|
isFailed,
|
|
245
193
|
})
|
|
246
194
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
stepLabel = labels.orderPlacedStep
|
|
256
|
-
} else if (step.key === 'payment' && stepStatus === 'loading') {
|
|
257
|
-
stepLabel = labels.paymentPendingStep
|
|
258
|
-
} else if (step.key === 'processing' && stepStatus === 'loading') {
|
|
259
|
-
stepLabel = labels.handlingStep
|
|
260
|
-
} else if (step.key === 'shipping' && stepStatus === 'completed') {
|
|
261
|
-
stepLabel = labels.invoicedStep
|
|
262
|
-
} else if (step.key === 'shipping' && stepStatus === 'loading') {
|
|
263
|
-
stepLabel = labels.deliveredStep
|
|
264
|
-
}
|
|
265
|
-
}
|
|
195
|
+
const stepLabel = getDisplayStepLabel({
|
|
196
|
+
stepKey: step.key,
|
|
197
|
+
stepStatus,
|
|
198
|
+
labels,
|
|
199
|
+
status,
|
|
200
|
+
statusLabels,
|
|
201
|
+
isCanceled,
|
|
202
|
+
})
|
|
266
203
|
|
|
267
204
|
// Add creation date to the order step when it's completed or failed
|
|
268
205
|
let completedAt: string | undefined
|
|
@@ -304,10 +241,10 @@ function StatusCard({
|
|
|
304
241
|
{step.completedAt && (
|
|
305
242
|
<div data-fs-shipping-step-details>
|
|
306
243
|
<span data-fs-shipping-step-date>
|
|
307
|
-
{formatDate(step.completedAt).date}
|
|
244
|
+
{formatDate(step.completedAt, locale).date}
|
|
308
245
|
</span>
|
|
309
246
|
<span data-fs-shipping-step-time>
|
|
310
|
-
{formatDate(step.completedAt).time}
|
|
247
|
+
{formatDate(step.completedAt, locale).time}
|
|
311
248
|
</span>
|
|
312
249
|
</div>
|
|
313
250
|
)}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getOrderStatusLabel,
|
|
3
|
+
type OrderStatusCmsLabels,
|
|
4
|
+
type OrderStatusKey,
|
|
5
|
+
} from 'src/utils/userOrderStatus'
|
|
6
|
+
import type { OrderStatusSectionLabels } from '../orderDetailsLabels'
|
|
7
|
+
|
|
8
|
+
export type StepStatus = 'completed' | 'loading' | 'not-started' | 'failed'
|
|
9
|
+
export type StepKey =
|
|
10
|
+
| 'order'
|
|
11
|
+
| 'approval'
|
|
12
|
+
| 'payment'
|
|
13
|
+
| 'processing'
|
|
14
|
+
| 'shipping'
|
|
15
|
+
|
|
16
|
+
export function getStepLabel(
|
|
17
|
+
stepKey: StepKey,
|
|
18
|
+
stepStatus: StepStatus,
|
|
19
|
+
labels: Required<OrderStatusSectionLabels>
|
|
20
|
+
): string {
|
|
21
|
+
const stepLabels: Record<StepKey, Record<StepStatus, string>> = {
|
|
22
|
+
order: {
|
|
23
|
+
completed: labels.orderPlacedStep,
|
|
24
|
+
loading: labels.orderPlacedStep,
|
|
25
|
+
'not-started': labels.orderPlacedStep,
|
|
26
|
+
failed: labels.orderPlacedStep,
|
|
27
|
+
},
|
|
28
|
+
approval: {
|
|
29
|
+
completed: labels.approvedStep,
|
|
30
|
+
loading: labels.pendingApprovalStep,
|
|
31
|
+
'not-started': labels.pendingApprovalStep,
|
|
32
|
+
failed: labels.deniedStep,
|
|
33
|
+
},
|
|
34
|
+
payment: {
|
|
35
|
+
completed: labels.paymentApprovedStep,
|
|
36
|
+
loading: labels.paymentPendingStep,
|
|
37
|
+
'not-started': labels.paymentAuthorizationStep,
|
|
38
|
+
failed: labels.paymentDeniedStep,
|
|
39
|
+
},
|
|
40
|
+
processing: {
|
|
41
|
+
completed: labels.readyForDeliveryStep,
|
|
42
|
+
loading: labels.handlingStep,
|
|
43
|
+
'not-started': labels.handlingStep,
|
|
44
|
+
failed: labels.canceledStep,
|
|
45
|
+
},
|
|
46
|
+
shipping: {
|
|
47
|
+
completed: labels.invoicedStep,
|
|
48
|
+
loading: labels.deliveredStep,
|
|
49
|
+
'not-started': labels.shipOrderStep,
|
|
50
|
+
failed: labels.canceledStep,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return stepLabels[stepKey][stepStatus]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getDisplayStepLabel({
|
|
58
|
+
stepKey,
|
|
59
|
+
stepStatus,
|
|
60
|
+
labels,
|
|
61
|
+
status,
|
|
62
|
+
statusLabels,
|
|
63
|
+
isCanceled,
|
|
64
|
+
}: {
|
|
65
|
+
stepKey: StepKey
|
|
66
|
+
stepStatus: StepStatus
|
|
67
|
+
labels: Required<OrderStatusSectionLabels>
|
|
68
|
+
status: OrderStatusKey
|
|
69
|
+
statusLabels?: OrderStatusCmsLabels
|
|
70
|
+
isCanceled: boolean
|
|
71
|
+
}): string {
|
|
72
|
+
if (isCanceled) {
|
|
73
|
+
return stepKey === 'payment'
|
|
74
|
+
? getOrderStatusLabel({ status, cmsLabels: statusLabels })
|
|
75
|
+
: '—'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
stepStatus === 'failed' &&
|
|
80
|
+
(stepKey === 'processing' || stepKey === 'shipping')
|
|
81
|
+
) {
|
|
82
|
+
return getOrderStatusLabel({
|
|
83
|
+
status: 'canceled',
|
|
84
|
+
cmsLabels: statusLabels,
|
|
85
|
+
statusFallback: labels.canceledStep,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (stepStatus === 'failed' && stepKey === 'payment') {
|
|
90
|
+
return getOrderStatusLabel({
|
|
91
|
+
status,
|
|
92
|
+
cmsLabels: statusLabels,
|
|
93
|
+
statusFallback: labels.paymentDeniedStep,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return getStepLabel(stepKey, stepStatus, labels)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function formatDate(date: string, locale: string) {
|
|
101
|
+
const dateObj = new Date(date)
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
date: new Intl.DateTimeFormat(locale, {
|
|
105
|
+
month: '2-digit',
|
|
106
|
+
day: '2-digit',
|
|
107
|
+
year: 'numeric',
|
|
108
|
+
}).format(dateObj),
|
|
109
|
+
time: new Intl.DateTimeFormat(locale, {
|
|
110
|
+
hour: 'numeric',
|
|
111
|
+
minute: '2-digit',
|
|
112
|
+
}).format(dateObj),
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type OrderSummarySectionLabels,
|
|
5
5
|
resolveOrderSummaryLabels,
|
|
6
6
|
} from '../orderDetailsLabels'
|
|
7
|
+
import { getOrderTotalLabel } from './getOrderTotalLabel'
|
|
7
8
|
|
|
8
9
|
// Interface for order totals (items, shipping, discounts)
|
|
9
10
|
// TODO: Use type from API
|
|
@@ -83,7 +84,7 @@ function SummaryCard({
|
|
|
83
84
|
<Card title={labels.summaryTitle} data-fs-order-summary-card>
|
|
84
85
|
{displayTotals.map((total) => (
|
|
85
86
|
<div key={total.id} data-fs-order-summary-item>
|
|
86
|
-
<span>{total
|
|
87
|
+
<span>{getOrderTotalLabel(total, labels)}</span>
|
|
87
88
|
<span>{formatPrice(total.value, currencyCode)}</span>
|
|
88
89
|
</div>
|
|
89
90
|
))}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { OrderSummarySectionLabels } from '../orderDetailsLabels'
|
|
2
|
+
|
|
3
|
+
const TOTAL_LABEL_KEYS = {
|
|
4
|
+
items: 'itemsTotalLabel',
|
|
5
|
+
discounts: 'discountsTotalLabel',
|
|
6
|
+
shipping: 'shippingTotalLabel',
|
|
7
|
+
tax: 'taxTotalLabel',
|
|
8
|
+
change: 'changeTotalLabel',
|
|
9
|
+
interest: 'interestLabel',
|
|
10
|
+
} as const satisfies Record<string, keyof OrderSummarySectionLabels>
|
|
11
|
+
|
|
12
|
+
export function getOrderTotalLabel(
|
|
13
|
+
total: { id: string; name: string },
|
|
14
|
+
labels: Required<OrderSummarySectionLabels>
|
|
15
|
+
): string {
|
|
16
|
+
const labelKey = TOTAL_LABEL_KEYS[total.id.toLowerCase()]
|
|
17
|
+
|
|
18
|
+
return labelKey ? labels[labelKey] : total.name
|
|
19
|
+
}
|