Skip to content

Commit d575257

Browse files
authored
Wishlist functionality (#162)
* created server action to toggle items on the wishlist * new method to fetch user wishlist * new method to check if product is in wishlist * getWishlist returns products with details * refactor wishlist functions * installed server-only package
1 parent 1733f9b commit d575257

File tree

5 files changed

+101
-0
lines changed

5 files changed

+101
-0
lines changed

app/_lib/SwellAPI.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { cookies } from 'next/headers';
22
import { redirect } from 'next/navigation';
3+
import 'server-only';
34

45
type RequestBody = {
56
query: string;
67
};
78

9+
type WishlistBody = {
10+
id: string;
11+
content: { wishlist_ids: string[] };
12+
};
13+
814
const SWELL_STORE_ID = process.env.SWELL_STORE_ID as string;
15+
const SWELL_SECRET_KEY = process.env.SWELL_SECRET_KEY as string;
916

1017
/*****************************************************************************
1118
* Extract Swell session cookie
@@ -41,6 +48,26 @@ const makeRequest = async (path: string, method = 'GET', body?: RequestBody) =>
4148
return response.json();
4249
};
4350

51+
/*****************************************************************************
52+
* Make Admin API request to Swell using secret key
53+
****************************************************************************/
54+
const makeAdminRequest = async (path: string, method = 'GET', body?: unknown) => {
55+
const requestHeaders: HeadersInit = new Headers();
56+
requestHeaders.set('Content-Type', 'application/json');
57+
requestHeaders.set(
58+
'Authorization',
59+
`Basic ${Buffer.from(`${SWELL_STORE_ID}:${SWELL_SECRET_KEY}`).toString('base64')}`
60+
);
61+
62+
const response = await fetch(`https://api.swell.store${path}`, {
63+
method: method,
64+
headers: requestHeaders,
65+
body: JSON.stringify(body)
66+
});
67+
68+
return response.json();
69+
};
70+
4471
/*****************************************************************************
4572
* Get required data in a single GraphQL request
4673
****************************************************************************/
@@ -173,3 +200,55 @@ const getCards = async () => {
173200

174201
return response?.results;
175202
};
203+
204+
/*****************************************************************************
205+
* Get logged user wishlist products ids
206+
****************************************************************************/
207+
const getWishlistIds = async (): Promise<string[]> => {
208+
const { content } = (await makeRequest('/api/account')) as WishlistBody;
209+
return content.wishlist_ids;
210+
};
211+
212+
/*****************************************************************************
213+
* Get logged user wishlist
214+
****************************************************************************/
215+
export const getWishlist = async (): Promise<Product[]> => {
216+
const productsIds = await getWishlistIds();
217+
218+
if (productsIds.length === 0) {
219+
return [];
220+
}
221+
222+
const { results } = (await makeRequest(
223+
`/api/products?where[id][$in]=${productsIds.join(',')}`
224+
)) as { results: Product[] };
225+
226+
return results;
227+
};
228+
229+
/*****************************************************************************
230+
* Check if product is in logged user wishlist
231+
****************************************************************************/
232+
export const isProductInWishlist = async (productId: string): Promise<boolean> => {
233+
const productsIds = await getWishlistIds();
234+
return productsIds.includes(productId);
235+
};
236+
237+
/*****************************************************************************
238+
* Add product to logged user wishlist
239+
****************************************************************************/
240+
export const toggleWishlist = async (productId: string): Promise<string[]> => {
241+
const { id, content } = (await makeRequest('/api/account')) as WishlistBody;
242+
243+
// Add or remove product depending on if it's already in the wishlist
244+
const wishlistIds = content.wishlist_ids.includes(productId)
245+
? content.wishlist_ids.filter((id) => id !== productId)
246+
: [...content.wishlist_ids, productId];
247+
248+
// Overwrite wishlist with new list of products
249+
const wishlist = (await makeAdminRequest(`/accounts/${id}`, 'PUT', {
250+
$set: { content: { wishlist_ids: wishlistIds } }
251+
})) as WishlistBody;
252+
253+
return wishlist.content.wishlist_ids;
254+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use server';
2+
3+
import { toggleWishlist } from '~/_lib/SwellAPI';
4+
5+
export async function toggleWishlistAction(productId: string) {
6+
return await toggleWishlist(productId);
7+
}

next.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const nextConfig = {
1919
reactStrictMode: true,
2020
images: {
2121
domains: ['cdn.schema.io', 'cdn-staging.swell.store']
22+
},
23+
experimental: {
24+
serverActions: true
2225
}
2326
};
2427

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"react-image-gallery": "^1.2.9",
2626
"react-rater": "^6.0.5",
2727
"react-toastify": "^9.1.0",
28+
"server-only": "^0.0.1",
2829
"swell-js": "^3.17.6",
2930
"swell-node": "^4.0.10",
3031
"swiper": "^8.3.2"

0 commit comments

Comments
 (0)