Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add olist provider #791

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ yarn-error.log*

# Turborepo
.turbo

#typescript
tsconfig.tsbuildinfo
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
- Kibo Commerce Demo: https://kibocommerce.vercel.store/
- Commerce.js Demo: https://commercejs.vercel.store/
- SalesForce Cloud Commerce Demo: https://salesforce-cloud-commerce.vercel.store/
- Olist Demo: https://olist.vercel.store/

## Run minimal version locally

Expand Down
2 changes: 1 addition & 1 deletion packages/bigcommerce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12.0.8",
"next": "^12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
43 changes: 24 additions & 19 deletions packages/commerce/new-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A commerce provider is a headless e-commerce platform that integrates with the [
- Kibo Commerce ([packages/kibocommerce](../kibocommerce))
- Commerce.js ([packages/commercejs](../commercejs))
- SFCC - SalesForce Cloud Commerce ([packages/sfcc](../sfcc))
- Olist ([packages/olist](../olist))

Adding a commerce provider means adding a new folder in `packages` with a folder structure like the next one:

Expand Down Expand Up @@ -69,7 +70,10 @@ Then, open [/site/.env.template](/site/.env.template) and add the provider name
Using BigCommerce as an example. The first thing to do is export a `CommerceProvider` component that includes a `provider` object with all the handlers that can be used for hooks:

```tsx
import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce'
import {
getCommerceProvider,
useCommerce as useCoreCommerce,
} from '@vercel/commerce'
import { bigcommerceProvider, BigcommerceProvider } from './provider'

export { bigcommerceProvider }
Expand Down Expand Up @@ -213,25 +217,26 @@ export const handler: MutationHook<AddItemHook> = {
```

## Showing progress and features

When creating a PR for a new provider, include this list in the PR description and mark the progress as you push so we can organize the code review. Not all points are required (but advised) so make sure to keep the list up to date.

**Status**

* [ ] CommerceProvider
* [ ] Schema & TS types
* [ ] API Operations - Get all collections
* [ ] API Operations - Get all pages
* [ ] API Operations - Get all products
* [ ] API Operations - Get page
* [ ] API Operations - Get product
* [ ] API Operations - Get Shop Info (categories and vendors working — `vendors` query still a WIP PR on Reaction)
* [ ] Hook - Add Item
* [ ] Hook - Remove Item
* [ ] Hook - Update Item
* [ ] Hook - Get Cart (account-tied carts working, anonymous carts working, cart reconciliation working)
* [ ] Auth (based on a WIP PR on Reaction - still need to implement refresh tokens)
* [ ] Customer information
* [ ] Product attributes - Size, Colors
* [ ] Custom checkout
* [ ] Typing (in progress)
* [ ] Tests
- [ ] CommerceProvider
- [ ] Schema & TS types
- [ ] API Operations - Get all collections
- [ ] API Operations - Get all pages
- [ ] API Operations - Get all products
- [ ] API Operations - Get page
- [ ] API Operations - Get product
- [ ] API Operations - Get Shop Info (categories and vendors working — `vendors` query still a WIP PR on Reaction)
- [ ] Hook - Add Item
- [ ] Hook - Remove Item
- [ ] Hook - Update Item
- [ ] Hook - Get Cart (account-tied carts working, anonymous carts working, cart reconciliation working)
- [ ] Auth (based on a WIP PR on Reaction - still need to implement refresh tokens)
- [ ] Customer information
- [ ] Product attributes - Size, Colors
- [ ] Custom checkout
- [ ] Typing (in progress)
- [ ] Tests
2 changes: 1 addition & 1 deletion packages/commerce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12.0.8",
"next": "^12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
1 change: 1 addition & 0 deletions packages/commerce/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { NextApiHandler } from 'next'
import type { FetchOptions, Response } from '@vercel/fetch'

import type { APIEndpoint, APIHandler } from './utils/types'
import type { CartSchema } from '../types/cart'
import type { CustomerSchema } from '../types/customer'
Expand Down
2 changes: 1 addition & 1 deletion packages/commercejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12.0.8",
"next": "^12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/kibocommerce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12.0.8",
"next": "^12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/local/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12.0.8",
"next": "^12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
4 changes: 4 additions & 0 deletions packages/olist/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
COMMERCE_PROVIDER=@vercel/commerce-olist

NEXT_PUBLIC_OLIST_STOREFRONT_DOMAIN=
NEXT_PUBLIC_OLIST_STOREFRONT_ACCESS_TOKEN=
2 changes: 2 additions & 0 deletions packages/olist/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
6 changes: 6 additions & 0 deletions packages/olist/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false
}
1 change: 1 addition & 0 deletions packages/olist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Next.js Olist Provider
80 changes: 80 additions & 0 deletions packages/olist/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"name": "@vercel/commerce-olist",
"version": "0.0.1",
"license": "MIT",
"scripts": {
"release": "taskr release",
"build": "taskr build",
"dev": "taskr",
"types": "tsc --emitDeclarationOnly",
"prettier-fix": "prettier --write ."
},
"sideEffects": false,
"type": "module",
"exports": {
".": "./dist/index.js",
"./*": [
"./dist/*.js",
"./dist/*/index.js"
],
"./next.config": "./dist/next.config.cjs"
},
"typesVersions": {
"*": {
"*": [
"src/*",
"src/*/index"
],
"next.config": [
"dist/next.config.d.cts"
]
}
},
"files": [
"dist"
],
"publishConfig": {
"typesVersions": {
"*": {
"*": [
"dist/*.d.ts",
"dist/*/index.d.ts"
],
"next.config": [
"dist/next.config.d.cts"
]
}
}
},
"dependencies": {
"@vercel/commerce": "^0.0.1",
"@vercel/fetch": "^6.1.1",
"@vnda/headless-framework": "^0.0.33"
},
"peerDependencies": {
"next": "^12",
"react": "^17",
"react-dom": "^17"
},
"devDependencies": {
"@taskr/clear": "^1.1.0",
"@taskr/esnext": "^1.1.0",
"@taskr/watch": "^1.1.0",
"@types/node": "^17.0.8",
"@types/react": "^17.0.38",
"lint-staged": "^12.1.7",
"next": "^12",
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"taskr": "^1.1.0",
"taskr-swc": "^0.0.1",
"typescript": "^4.5.4"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx,json}": [
"prettier --write",
"git add"
]
}
}
84 changes: 84 additions & 0 deletions packages/olist/src/api/endpoints/cart/add-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { serialize } from 'cookie'
import type { Cart } from '@vnda/headless-framework'

import type { CartEndpoint, Handler } from '.'

import {
mapCommerceToRawRequest,
mapRawToCommerceResponse,
} from '../../../utils/cart'

const addItem: CartEndpoint['handlers']['addItem'] = async ({
res: response,
body: { cartId, item },
config: { service, cartCookie, cartTokenCookie },
}: Handler) => {
try {
if (!item) {
return response.status(400).json({
data: null,
errors: [{ message: 'Missing item' }],
})
}

if (!item.quantity) item.quantity = 1

let cart: Cart

if (!cartId) {
cart = await service.cart.create()

response.setHeader('Set-Cookie', [
serialize(cartCookie, cart.id.toString(), {
maxAge: 60 * 60 * 24 * 30,
expires: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000),
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
}),
serialize(cartTokenCookie, cart.token, {
maxAge: 60 * 60 * 24 * 30,
expires: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000),
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
}),
])
} else {
cart = await service.cart.getById(Number(cartId))
}

const itemExistQuantity =
(
cart.items.find(({ variantSku }) => variantSku === item.variantId)! ||
[]
).quantity || 0

const cartItem = await service.cart.addItem(
cartId ? Number(cartId) : cart!.id,
mapCommerceToRawRequest({
...item,
quantity: Number(itemExistQuantity) + 1 || 1,
})
)

cart.items = [
...(cart?.items.map((value) =>
value.variantSku === item.variantId ? cartItem : value
) || []),
...(itemExistQuantity ? [] : [cartItem]),
]

response.status(200).json({
data: mapRawToCommerceResponse(cart),
errors: [],
})
} catch (error) {
response.status(500).json({
data: {},
errors: error,
})
}
}

export default addItem
41 changes: 41 additions & 0 deletions packages/olist/src/api/endpoints/cart/get-cart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { serialize } from 'cookie'

import type { CartEndpoint, Handler } from '.'

import { mapRawToCommerceResponse } from '../../../utils/cart'

const getCart: CartEndpoint['handlers']['getCart'] = async ({
res: response,
body: { cartId },
config: { service, cartCookie, cartTokenCookie },
}: Handler) => {
if (!cartId) {
return response.status(400).json({
data: null,
errors: [{ message: 'Invalid request' }],
})
}

try {
const cart = await service.cart.getById(Number(cartId))

response
.status(200)
.json({ data: mapRawToCommerceResponse(cart), errors: [] })
} catch (error) {
response.setHeader('Set-Cookie', [
serialize(cartCookie, cartId, {
maxAge: -1,
path: '/',
}),
serialize(cartTokenCookie, cartId, {
maxAge: -1,
path: '/',
}),
])

response.status(200).json({ data: null, errors: [] })
}
}

export default getCart
Loading