Skip to content

Commit 0995486

Browse files
feat: validate voucher
1 parent 5093179 commit 0995486

File tree

11 files changed

+61
-30
lines changed

11 files changed

+61
-30
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ActionFunction, ActionFunctionArgs } from '@remix-run/node';
2+
import { getStoreFront } from '~/use-cases/storefront.server';
3+
import { privateJson } from '~/core/bridge/privateJson.server';
4+
import { getContext } from '~/use-cases/http/utils';
5+
import { validateVoucher } from '~/use-cases/crystallize/read/validateVoucher.server';
6+
7+
export const action: ActionFunction = async ({ request }: ActionFunctionArgs) => {
8+
const requestContext = getContext(request);
9+
const { secret: storefront } = await getStoreFront(requestContext.host);
10+
const body = await request.json();
11+
12+
const checkVoucher = await validateVoucher(body.voucher, {
13+
apiClient: storefront.apiClient,
14+
});
15+
16+
return privateJson({
17+
isValid: checkVoucher,
18+
});
19+
};

shared/application/translations/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
"clone": "Clone previous cart",
5555
"voucherCode": "Voucher code",
5656
"useVoucher": "Use voucher",
57-
"voucherApplied": "Voucher applied"
57+
"voucherApplied": "Voucher applied.",
58+
"voucherInvalid": "Voucher is invalid."
5859
},
5960
"dimensions": "Dimensions",
6061
"specifications": "Manuals and specifications",

shared/application/translations/fr.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
"clone": "Cloner le caddie précédent",
5555
"voucherCode": "Bon de réduction",
5656
"useVoucher": "Utiliser bon de réduction",
57-
"voucherApplied": "Bon de réduction appliqué"
57+
"voucherApplied": "Bon de réduction appliqué.",
58+
"voucherInvalid": "Le bon de réduction est invalide."
5859
},
5960
"dimensions": "Dimensions",
6061
"specifications": "Manuels et caractéristiques",

shared/application/translations/no.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
"clone": "Klone forrige handlekurv",
5555
"voucherCode": "Rabatt code",
5656
"useVoucher": "Bruk rabatt",
57-
"voucherApplied": "Rabatt brukt"
57+
"voucherApplied": "Rabatt brukt.",
58+
"voucherInvalid": "Rabattkoden er ugyldig."
5859
},
5960
"dimensions": "Dimensjoner",
6061
"specifications": "Manualer og spesifikasjoner",

shared/application/ui/components/voucher.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@ import { useRemoteCart } from '../hooks/useRemoteCart';
88
import { Input } from './input';
99

1010
export const VoucherForm: React.FC = () => {
11-
const { cart: localCart, setVoucher } = useLocalCart();
12-
const { loading, remoteCart } = useRemoteCart();
11+
const { cart: localCart, setVoucher, validateVoucher } = useLocalCart();
12+
const { loading } = useRemoteCart();
1313
const [voucherValue, setVoucherValue] = useState(localCart?.extra?.voucher ?? '');
14-
15-
const isVoucherValid = remoteCart?.context?.price?.voucherCode ? true : false;
14+
const [showMessage, setShowMessage] = useState('');
1615

1716
const { _t } = useAppContext();
1817

18+
const checkVoucher = async (voucher: string) => {
19+
if (!voucher) return;
20+
const checkVoucher = await validateVoucher(voucher);
21+
return checkVoucher.isValid
22+
? setShowMessage(_t('cart.voucherApplied'))
23+
: setShowMessage(_t('cart.voucherInvalid'));
24+
};
25+
1926
return (
2027
<ClientOnly>
2128
<div className="flex flex-col w-full">
@@ -36,6 +43,7 @@ export const VoucherForm: React.FC = () => {
3643
className="bg-[#000] text-[#fff] px-2 py-1 rounded mt-5 text-center h-10"
3744
onClick={() => {
3845
setVoucher(voucherValue);
46+
checkVoucher(voucherValue);
3947
}}
4048
>
4149
{loading && localCart?.extra?.voucher ? _t('loading') : _t('cart.useVoucher')}
@@ -47,13 +55,14 @@ export const VoucherForm: React.FC = () => {
4755
onClick={() => {
4856
setVoucherValue('');
4957
setVoucher('');
58+
setShowMessage('');
5059
}}
5160
>
5261
{_t('delete')}
5362
</button>
5463
)}
5564
</div>
56-
{isVoucherValid ? <div className="text-sm my-2 text-green2">{_t('cart.voucherApplied')}</div> : null}
65+
{showMessage && <p className="text-sm mt-2 text-[#666]">{showMessage}</p>}
5766
</div>
5867
</ClientOnly>
5968
);

shared/application/ui/hooks/useLocalCart.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use client';
22
import { useLocalStorage, writeStorage } from '@rehooks/local-storage';
33
import { LocalCart } from '~/use-cases/contracts/LocalCart';
4+
import { ServiceAPI } from '~/use-cases/service-api';
5+
import { useAppContext } from '../app-context/provider';
46

57
const InitializeEmptyLocalCart = (): LocalCart => {
68
return {
@@ -18,6 +20,7 @@ export function useLocalCart() {
1820
...cart,
1921
});
2022
};
23+
const { state: appContextState } = useAppContext();
2124

2225
const isImmutable = () => {
2326
return cart.state === 'placed';
@@ -106,5 +109,13 @@ export function useLocalCart() {
106109
},
107110
});
108111
},
112+
validateVoucher: async (voucher: string) => {
113+
const api = ServiceAPI({
114+
language: appContextState.language,
115+
serviceApiUrl: appContextState.serviceApiUrl,
116+
});
117+
118+
return await api.validateVoucher(voucher);
119+
},
109120
};
110121
}

shared/application/use-cases/checkout/handleSaveCart.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ClientInterface } from '@crystallize/js-api-client';
22
import { hydrateCart } from '../crystallize/write/editCart';
3-
import { validateVoucher } from '../crystallize/read/validateVoucher.server';
43

54
type Deps = {
65
apiClient: ClientInterface;
@@ -14,23 +13,14 @@ export default async (body: any, { apiClient }: Deps, markets?: string[]) => {
1413
quantity: item.quantity,
1514
}));
1615

17-
const voucher = body.extra?.voucher?.toUpperCase();
18-
let validVoucher = null;
19-
20-
if (voucher) {
21-
try {
22-
validVoucher = await validateVoucher(voucher, { apiClient });
23-
} catch (error) {
24-
console.error('Voucher validation failed:', error);
25-
}
26-
}
16+
const voucher = body.extra?.voucher?.toUpperCase() || '';
2717

2818
try {
29-
return await hydrateCart(localCartItems, { apiClient }, cartId, markets, validVoucher ? voucher : '');
19+
return await hydrateCart(localCartItems, { apiClient }, cartId, markets, voucher);
3020
} catch (error: any) {
3121
if (error.message.includes('placed')) {
3222
console.log('Cart has been placed, creating a new one');
33-
return await hydrateCart(localCartItems, { apiClient }, undefined, markets, validVoucher ? voucher : '');
23+
return await hydrateCart(localCartItems, { apiClient }, undefined, markets, voucher);
3424
}
3525
throw error;
3626
}

shared/application/use-cases/contracts/RemoteCart.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export type Cart = {
1515
discounts: number[];
1616
currency: string;
1717
};
18-
context: {
18+
state: 'cart' | 'placed' | 'ordered';
19+
orderId?: string;
20+
context?: {
1921
price: {
2022
markets?: string[];
2123
decimals: number;
@@ -25,6 +27,4 @@ export type Cart = {
2527
voucherCode?: string;
2628
};
2729
};
28-
state: 'cart' | 'placed' | 'ordered';
29-
orderId?: string;
3030
};

shared/application/use-cases/crystallize/read/fetchCart.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export const fetchCart = async (cartId: string, { apiClient }: Deps): Promise<Ca
1414
},
1515
id: true,
1616
state: true,
17-
context: true,
1817
orderId: true,
1918
customer: {
2019
isGuest: true,

shared/application/use-cases/crystallize/write/editCart.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,11 @@ export const hydrateCart = async (
2828
selectedVariantIdentifier: 'sales',
2929
fallbackVariantIdentifiers: ['default'],
3030
compareAtVariantIdentifier: 'default',
31+
voucherCode,
3132
},
3233
},
3334
};
3435

35-
if (voucherCode) {
36-
input.context.price.voucherCode = voucherCode;
37-
}
38-
3936
if (cartId) {
4037
input.id = cartId;
4138
}
@@ -48,7 +45,6 @@ export const hydrateCart = async (
4845
},
4946
id: true,
5047
state: true,
51-
context: true,
5248
items: {
5349
quantity: true,
5450
variant: {

0 commit comments

Comments
 (0)