@graphcommerce/magento-payment-tokens 9.1.0-canary.17
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/components/CustomerTokensPage/CustomerTokensPage.tsx +134 -0
- package/components/DeletePaymentTokenButton.tsx +28 -0
- package/components/PaymentTokensAccountMenuItem/PaymentTokensAccountMenuItem.tsx +31 -0
- package/components/index.ts +2 -0
- package/copy/pages/account/payment-tokens.tsx +29 -0
- package/graphql/fragments/PaymentToken.graphql +6 -0
- package/graphql/inject/AccountDashboardPaymentTokens.graphql +8 -0
- package/graphql/mutations/DeletePaymentToken.graphql +10 -0
- package/graphql/queries/PaymentTokens.graphql +7 -0
- package/index.ts +5 -0
- package/package.json +35 -0
- package/plugins/PaymentTokensAccountMenuItem.tsx +19 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ApolloClient, NormalizedCacheObject } from '@graphcommerce/graphql'
|
|
2
|
+
import {
|
|
3
|
+
getCustomerAccountIsDisabled,
|
|
4
|
+
useCustomerQuery,
|
|
5
|
+
WaitForCustomer,
|
|
6
|
+
} from '@graphcommerce/magento-customer'
|
|
7
|
+
import { PageMeta, StoreConfigDocument } from '@graphcommerce/magento-store'
|
|
8
|
+
import {
|
|
9
|
+
breakpointVal,
|
|
10
|
+
filterNonNullableKeys,
|
|
11
|
+
iconCreditCard,
|
|
12
|
+
LayoutOverlayHeader,
|
|
13
|
+
LayoutTitle,
|
|
14
|
+
sxx,
|
|
15
|
+
type GetStaticProps,
|
|
16
|
+
} from '@graphcommerce/next-ui'
|
|
17
|
+
import { i18n } from '@lingui/core'
|
|
18
|
+
import { Trans } from '@lingui/macro'
|
|
19
|
+
import { Alert, Box, Container, lighten, Typography } from '@mui/material'
|
|
20
|
+
import type { PaymentTokenFragment } from '../../graphql/fragments/PaymentToken.gql'
|
|
21
|
+
import { CustomerPaymentTokensDocument } from '../../graphql/queries/PaymentTokens.gql'
|
|
22
|
+
import { DeletePaymentTokenButton } from '../DeletePaymentTokenButton'
|
|
23
|
+
|
|
24
|
+
export type CustomerTokensPageProps = Record<string, unknown>
|
|
25
|
+
type CustomerTokensGetStaticProps = GetStaticProps<Record<string, unknown>, CustomerTokensPageProps>
|
|
26
|
+
|
|
27
|
+
export function CustomerTokensPage() {
|
|
28
|
+
const dashboard = useCustomerQuery(CustomerPaymentTokensDocument, {
|
|
29
|
+
fetchPolicy: 'cache-and-network',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const loading = dashboard.loading && !dashboard.previousData
|
|
33
|
+
|
|
34
|
+
const paymentTokens = filterNonNullableKeys(
|
|
35
|
+
dashboard.data?.customerPaymentTokens?.items ??
|
|
36
|
+
dashboard.previousData?.customerPaymentTokens?.items,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<LayoutOverlayHeader>
|
|
42
|
+
<LayoutTitle size='small' component='span' icon={iconCreditCard}>
|
|
43
|
+
<Trans id='Payment information'>Payment information</Trans>
|
|
44
|
+
</LayoutTitle>
|
|
45
|
+
</LayoutOverlayHeader>
|
|
46
|
+
|
|
47
|
+
<Container maxWidth='md'>
|
|
48
|
+
<PageMeta title={i18n._('Payment information')} metaRobots={['noindex']} />
|
|
49
|
+
|
|
50
|
+
<LayoutTitle icon={iconCreditCard} sx={(theme) => ({ mb: theme.spacings.xs })}>
|
|
51
|
+
<Trans id='Stored payment methods'>Stored payment methods</Trans>
|
|
52
|
+
</LayoutTitle>
|
|
53
|
+
|
|
54
|
+
<WaitForCustomer waitFor={!loading}>
|
|
55
|
+
{!paymentTokens?.length && (
|
|
56
|
+
<Alert severity='info'>
|
|
57
|
+
<Trans id='No stored payment methods found.'>No stored payment methods found.</Trans>
|
|
58
|
+
</Alert>
|
|
59
|
+
)}
|
|
60
|
+
|
|
61
|
+
<Box sx={sxx((theme) => ({ display: 'grid', rowGap: theme.spacings.xs }))}>
|
|
62
|
+
{paymentTokens?.map((token) => (
|
|
63
|
+
<Box
|
|
64
|
+
key={token.public_hash}
|
|
65
|
+
sx={(theme) => ({
|
|
66
|
+
px: theme.spacings.xxs,
|
|
67
|
+
py: theme.spacings.xxs,
|
|
68
|
+
background:
|
|
69
|
+
theme.palette.mode === 'light'
|
|
70
|
+
? theme.palette.background.default
|
|
71
|
+
: lighten(theme.palette.background.default, 0.15),
|
|
72
|
+
...breakpointVal(
|
|
73
|
+
'borderRadius',
|
|
74
|
+
theme.shape.borderRadius * 2,
|
|
75
|
+
theme.shape.borderRadius * 3,
|
|
76
|
+
theme.breakpoints.values,
|
|
77
|
+
),
|
|
78
|
+
display: 'grid',
|
|
79
|
+
gridTemplate: `
|
|
80
|
+
"details delete"
|
|
81
|
+
"type method" / 1fr auto
|
|
82
|
+
`,
|
|
83
|
+
rowGap: 0.5,
|
|
84
|
+
columnGap: 1,
|
|
85
|
+
})}
|
|
86
|
+
>
|
|
87
|
+
<Box sx={{ gridArea: 'details', color: 'text.secondary' }}>{token.details}</Box>
|
|
88
|
+
|
|
89
|
+
<Typography
|
|
90
|
+
variant='subtitle1'
|
|
91
|
+
noWrap
|
|
92
|
+
sx={{ gridArea: 'type', overflow: 'hidden', textOverflow: 'ellipsis' }}
|
|
93
|
+
>
|
|
94
|
+
{token.type}
|
|
95
|
+
</Typography>
|
|
96
|
+
|
|
97
|
+
{/* <Box sx={{ gridArea: 'method', color: 'text.secondary' }}>
|
|
98
|
+
{token.payment_method_code}
|
|
99
|
+
</Box> */}
|
|
100
|
+
|
|
101
|
+
{token.details && (
|
|
102
|
+
<Box sx={{ gridArea: 'details', color: 'text.secondary' }}>{token.details}</Box>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<DeletePaymentTokenButton
|
|
106
|
+
sx={{ gridArea: 'delete' }}
|
|
107
|
+
publicHash={token.public_hash}
|
|
108
|
+
/>
|
|
109
|
+
</Box>
|
|
110
|
+
))}
|
|
111
|
+
</Box>
|
|
112
|
+
</WaitForCustomer>
|
|
113
|
+
</Container>
|
|
114
|
+
</>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function createGetStaticProps(
|
|
119
|
+
client: ApolloClient<NormalizedCacheObject>,
|
|
120
|
+
): CustomerTokensGetStaticProps {
|
|
121
|
+
return async (context) => {
|
|
122
|
+
if (getCustomerAccountIsDisabled(context.locale)) return { notFound: true }
|
|
123
|
+
|
|
124
|
+
const conf = client.query({ query: StoreConfigDocument })
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
props: {
|
|
128
|
+
apolloState: await conf.then(() => client.cache.extract()),
|
|
129
|
+
variantMd: 'bottom',
|
|
130
|
+
up: { href: '/account', title: i18n._('Account') },
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ApolloErrorSnackbar, useFormGqlMutation } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { Fab, iconBin, sxx } from '@graphcommerce/next-ui'
|
|
3
|
+
import { t } from '@lingui/macro'
|
|
4
|
+
import { type SxProps, type Theme } from '@mui/material'
|
|
5
|
+
import { DeletePaymentTokenDocument } from '../graphql/mutations/DeletePaymentToken.gql'
|
|
6
|
+
|
|
7
|
+
export function DeletePaymentTokenButton(props: { sx: SxProps<Theme>; publicHash: string }) {
|
|
8
|
+
const { sx, publicHash } = props
|
|
9
|
+
|
|
10
|
+
const { handleSubmit, formState, error } = useFormGqlMutation(DeletePaymentTokenDocument, {
|
|
11
|
+
values: { publicHash },
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const submit = handleSubmit(() => {})
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<form onSubmit={submit}>
|
|
18
|
+
<Fab
|
|
19
|
+
type='submit'
|
|
20
|
+
aria-label={t`Remove`}
|
|
21
|
+
loading={formState.isSubmitting}
|
|
22
|
+
icon={iconBin}
|
|
23
|
+
sx={sxx({ boxShadow: 0 }, sx)}
|
|
24
|
+
/>
|
|
25
|
+
<ApolloErrorSnackbar error={error} />
|
|
26
|
+
</form>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccountDashboardDocument,
|
|
3
|
+
AccountMenuItem,
|
|
4
|
+
useCustomerQuery,
|
|
5
|
+
type AccountMenuItemProps,
|
|
6
|
+
} from '@graphcommerce/magento-customer'
|
|
7
|
+
import { iconCreditCard } from '@graphcommerce/next-ui'
|
|
8
|
+
import { Trans } from '@lingui/macro'
|
|
9
|
+
import type { SetOptional } from 'type-fest'
|
|
10
|
+
|
|
11
|
+
type PaymentTokensAccountMenuItemProps = SetOptional<
|
|
12
|
+
Omit<AccountMenuItemProps, 'href'>,
|
|
13
|
+
'iconSrc' | 'title'
|
|
14
|
+
>
|
|
15
|
+
|
|
16
|
+
export function PaymentTokensAccountMenuItem(props: PaymentTokensAccountMenuItemProps) {
|
|
17
|
+
const dashboard = useCustomerQuery(AccountDashboardDocument, { fetchPolicy: 'cache-only' })
|
|
18
|
+
const tokens = dashboard.data?.customerPaymentTokens?.items
|
|
19
|
+
|
|
20
|
+
if (!tokens || tokens.length === 0) return null
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<AccountMenuItem
|
|
24
|
+
href='/account/payment-tokens'
|
|
25
|
+
iconSrc={iconCreditCard}
|
|
26
|
+
title={<Trans id='Stored payment methods'>Stored payment methods</Trans>}
|
|
27
|
+
subtitle={<Trans id='For faster checkout'>For faster checkout</Trans>}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
+
import type { PageOptions } from '@graphcommerce/framer-next-pages'
|
|
3
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
4
|
+
import {
|
|
5
|
+
createGetStaticProps,
|
|
6
|
+
CustomerTokensPage,
|
|
7
|
+
type CustomerTokensPageProps,
|
|
8
|
+
} from '@graphcommerce/magento-payment-tokens'
|
|
9
|
+
import type { GetStaticProps, LayoutOverlayProps } from '@graphcommerce/next-ui'
|
|
10
|
+
import { LayoutOverlay } from '@graphcommerce/next-ui'
|
|
11
|
+
import { graphqlSharedClient } from '../../lib/graphql/graphqlSsrClient'
|
|
12
|
+
|
|
13
|
+
type GetPageStaticProps = GetStaticProps<LayoutOverlayProps>
|
|
14
|
+
|
|
15
|
+
export default function Page(props: CustomerTokensPageProps) {
|
|
16
|
+
return <CustomerTokensPage {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const pageOptions: PageOptions<LayoutOverlayProps> = {
|
|
20
|
+
overlayGroup: 'account',
|
|
21
|
+
Layout: LayoutOverlay,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Page.pageOptions = pageOptions
|
|
25
|
+
|
|
26
|
+
export const getStaticProps: GetPageStaticProps = async (context) => {
|
|
27
|
+
const client = graphqlSharedClient(context)
|
|
28
|
+
return createGetStaticProps(client)(context)
|
|
29
|
+
}
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/magento-payment-tokens",
|
|
3
|
+
"homepage": "https://www.graphcommerce.org/",
|
|
4
|
+
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
+
"version": "9.1.0-canary.17",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
|
+
"eslintConfig": {
|
|
9
|
+
"extends": "@graphcommerce/eslint-config-pwa",
|
|
10
|
+
"parserOptions": {
|
|
11
|
+
"project": "./tsconfig.json"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@graphcommerce/ecommerce-ui": "^9.1.0-canary.17",
|
|
16
|
+
"@graphcommerce/eslint-config-pwa": "^9.1.0-canary.17",
|
|
17
|
+
"@graphcommerce/graphql": "^9.1.0-canary.17",
|
|
18
|
+
"@graphcommerce/image": "^9.1.0-canary.17",
|
|
19
|
+
"@graphcommerce/magento-cart": "^9.1.0-canary.17",
|
|
20
|
+
"@graphcommerce/magento-cart-payment-method": "^9.1.0-canary.17",
|
|
21
|
+
"@graphcommerce/magento-customer": "^9.1.0-canary.17",
|
|
22
|
+
"@graphcommerce/magento-store": "^9.1.0-canary.17",
|
|
23
|
+
"@graphcommerce/next-ui": "^9.1.0-canary.17",
|
|
24
|
+
"@graphcommerce/prettier-config-pwa": "^9.1.0-canary.17",
|
|
25
|
+
"@graphcommerce/typescript-config-pwa": "^9.1.0-canary.17",
|
|
26
|
+
"@lingui/core": "^4.2.1",
|
|
27
|
+
"@lingui/macro": "^4.2.1",
|
|
28
|
+
"@lingui/react": "^4.2.1",
|
|
29
|
+
"@mui/material": "^5.10.16",
|
|
30
|
+
"framer-motion": "^11.0.0",
|
|
31
|
+
"next": "*",
|
|
32
|
+
"react": "^18.2.0",
|
|
33
|
+
"react-dom": "^18.2.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AccountMenuItemProps } from '@graphcommerce/magento-customer'
|
|
2
|
+
import type { PluginConfig, PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { PaymentTokensAccountMenuItem } from '../components'
|
|
4
|
+
|
|
5
|
+
export const config: PluginConfig = {
|
|
6
|
+
module: '@graphcommerce/magento-customer',
|
|
7
|
+
type: 'component',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function AccountMenuItem(props: PluginProps<AccountMenuItemProps>) {
|
|
11
|
+
const { Prev, href, ...rest } = props
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
<Prev {...rest} href={href} />
|
|
16
|
+
{href === '/account/orders' && <PaymentTokensAccountMenuItem />}
|
|
17
|
+
</>
|
|
18
|
+
)
|
|
19
|
+
}
|