Skip to content

Commit 5093179

Browse files
feat: add voucher validation
1 parent ec37262 commit 5093179

File tree

9 files changed

+71
-9
lines changed

9 files changed

+71
-9
lines changed

shared/application/translations/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
"immutable": "You have initiated, finalized or cancelled a payment, this version of your cart is therefore locked.",
5454
"clone": "Clone previous cart",
5555
"voucherCode": "Voucher code",
56-
"useVoucher": "Use voucher"
56+
"useVoucher": "Use voucher",
57+
"voucherApplied": "Voucher applied"
5758
},
5859
"dimensions": "Dimensions",
5960
"specifications": "Manuals and specifications",

shared/application/translations/fr.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
"immutable": "Vous avez initié, finalisé ou annulé un paiement, cette version de votre caddie est donc bloquée.",
5454
"clone": "Cloner le caddie précédent",
5555
"voucherCode": "Bon de réduction",
56-
"useVoucher": "Utiliser bon de réduction"
56+
"useVoucher": "Utiliser bon de réduction",
57+
"voucherApplied": "Bon de réduction appliqué"
5758
},
5859
"dimensions": "Dimensions",
5960
"specifications": "Manuels et caractéristiques",

shared/application/translations/no.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
"immutable": "Du har igangsatt, avsluttet eller kansellert en betaling, denne versjonen av handlekurven din er derfor låst.",
5454
"clone": "Klone forrige handlekurv",
5555
"voucherCode": "Rabatt code",
56-
"useVoucher": "Bruk rabatt"
56+
"useVoucher": "Bruk rabatt",
57+
"voucherApplied": "Rabatt brukt"
5758
},
5859
"dimensions": "Dimensjoner",
5960
"specifications": "Manualer og spesifikasjoner",

shared/application/ui/components/voucher.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import { Input } from './input';
99

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

1619
return (
@@ -50,6 +53,7 @@ export const VoucherForm: React.FC = () => {
5053
</button>
5154
)}
5255
</div>
56+
{isVoucherValid ? <div className="text-sm my-2 text-green2">{_t('cart.voucherApplied')}</div> : null}
5357
</div>
5458
</ClientOnly>
5559
);

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

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

45
type Deps = {
56
apiClient: ClientInterface;
@@ -13,14 +14,23 @@ export default async (body: any, { apiClient }: Deps, markets?: string[]) => {
1314
quantity: item.quantity,
1415
}));
1516

16-
const voucher = body.extra?.voucher?.toUpperCase() || '';
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+
}
1727

1828
try {
19-
return await hydrateCart(localCartItems, { apiClient }, cartId, markets, voucher);
29+
return await hydrateCart(localCartItems, { apiClient }, cartId, markets, validVoucher ? voucher : '');
2030
} catch (error: any) {
2131
if (error.message.includes('placed')) {
2232
console.log('Cart has been placed, creating a new one');
23-
return await hydrateCart(localCartItems, { apiClient }, undefined, markets, voucher);
33+
return await hydrateCart(localCartItems, { apiClient }, undefined, markets, validVoucher ? voucher : '');
2434
}
2535
throw error;
2636
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ export type Cart = {
1515
discounts: number[];
1616
currency: string;
1717
};
18+
context: {
19+
price: {
20+
markets?: string[];
21+
decimals: number;
22+
selectedVariantIdentifier: string;
23+
fallbackVariantIdentifiers: string[];
24+
compareAtVariantIdentifier: string;
25+
voucherCode?: string;
26+
};
27+
};
1828
state: 'cart' | 'placed' | 'ordered';
1929
orderId?: string;
2030
};

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const fetchCart = async (cartId: string, { apiClient }: Deps): Promise<Ca
1414
},
1515
id: true,
1616
state: true,
17+
context: true,
1718
orderId: true,
1819
customer: {
1920
isGuest: true,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { ClientInterface } from '@crystallize/js-api-client';
2+
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
3+
4+
type Deps = {
5+
apiClient: ClientInterface;
6+
};
7+
8+
export const validateVoucher = async (voucher: string, { apiClient }: Deps) => {
9+
const query = {
10+
validateVoucher: {
11+
__args: {
12+
voucher,
13+
},
14+
isValid: true,
15+
},
16+
};
17+
18+
const rawQuery = jsonToGraphQLQuery({ query });
19+
20+
try {
21+
const response = await apiClient.shopCartApi(rawQuery);
22+
return response.validateVoucher.isValid;
23+
} catch (exception: any) {
24+
console.error('Failed to fetch cart', exception.message);
25+
return {
26+
valid: false,
27+
};
28+
}
29+
};

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ type Deps = {
88

99
type Input = {
1010
items: Array<{ sku: string; quantity: number }>;
11-
context: Record<string, unknown>;
11+
context: Cart['context'];
1212
id?: string;
1313
};
14+
1415
export const hydrateCart = async (
1516
items: Array<{ sku: string; quantity: number }>,
1617
{ apiClient }: Deps,
@@ -27,11 +28,14 @@ export const hydrateCart = async (
2728
selectedVariantIdentifier: 'sales',
2829
fallbackVariantIdentifiers: ['default'],
2930
compareAtVariantIdentifier: 'default',
30-
voucherCode,
3131
},
3232
},
3333
};
3434

35+
if (voucherCode) {
36+
input.context.price.voucherCode = voucherCode;
37+
}
38+
3539
if (cartId) {
3640
input.id = cartId;
3741
}
@@ -44,6 +48,7 @@ export const hydrateCart = async (
4448
},
4549
id: true,
4650
state: true,
51+
context: true,
4752
items: {
4853
quantity: true,
4954
variant: {

0 commit comments

Comments
 (0)