Merge branch 'main' of https://github.com/vercel/commerce into turbo

This commit is contained in:
LFades 2022-01-17 01:31:44 -05:00
commit 0c8c668fdd
40 changed files with 244 additions and 24768 deletions

View File

@ -146,7 +146,7 @@ If your project was started with a "Deploy with Vercel" button, you can use Verc
2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link`
3. Download your environment variables: `vercel env pull .env.local`
Next, you're free to customize the starter. More updates coming soon. Stay tuned.
Next, you're free to customize the starter. More updates coming soon. Stay tuned..
</details>

24506
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,15 +27,15 @@ export const handler: MutationHook<LoginHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -32,15 +32,15 @@ export const handler: MutationHook<SignupHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -17,7 +17,7 @@ export const handler: MutationHook<AddItemHook> = {
({ fetch }) =>
() => {
const { data: customer } = useCustomer()
const { revalidate } = useWishlist()
const { mutate } = useWishlist()
return useCallback(
async function addItem(item) {
@ -30,10 +30,10 @@ export const handler: MutationHook<AddItemHook> = {
// TODO: add validations before doing the fetch
const data = await fetch({ input: { item } })
await revalidate()
await mutate()
return data
},
[fetch, revalidate, customer]
[fetch, mutate, customer]
)
},
}

View File

@ -19,7 +19,7 @@ export const handler: MutationHook<RemoveItemHook> = {
({ fetch }) =>
({ wishlist } = {}) => {
const { data: customer } = useCustomer()
const { revalidate } = useWishlist(wishlist)
const { mutate } = useWishlist(wishlist)
return useCallback(
async function removeItem(input) {
@ -31,10 +31,10 @@ export const handler: MutationHook<RemoveItemHook> = {
}
const data = await fetch({ input: { itemId: String(input.id) } })
await revalidate()
await mutate()
return data
},
[fetch, revalidate, customer]
[fetch, mutate, customer]
)
},
}

View File

@ -1,4 +1,4 @@
import type { ConfigInterface } from 'swr'
import type { SWRConfiguration } from 'swr'
import type { CommerceError } from './errors'
import type { ResponseState } from './use-data'
@ -10,10 +10,9 @@ export type Override<T, K> = Omit<T, keyof K> & K
/**
* Returns the properties in T with the properties in type K changed from optional to required
*/
export type PickRequired<T, K extends keyof T> = Omit<T, K> &
{
[P in K]-?: NonNullable<T[P]>
}
export type PickRequired<T, K extends keyof T> = Omit<T, K> & {
[P in K]-?: NonNullable<T[P]>
}
/**
* Core fetcher added by CommerceProvider
@ -141,7 +140,7 @@ export type MutationHookContext<H extends MutationSchemaBase> = {
: (context: { input: H['fetcherInput'] }) => H['data'] | Promise<H['data']>
}
export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface<
export type SwrOptions<Data, Input = null, Result = any> = SWRConfiguration<
Data,
CommerceError,
HookFetcher<Data, Input, Result>

View File

@ -18,7 +18,7 @@ export default function getAllProductsOperation({
})
const productsFormatted =
data?.map((product) => normalizeProduct(product)) || []
data?.map((product: any) => normalizeProduct(product)) || []
return {
products: productsFormatted,

View File

@ -12,8 +12,10 @@ export default async function sdkFetch<
>(
resource: Resource,
method: Method,
...variables: Parameters<Commerce[Resource][Method]>
): Promise<ReturnType<Commerce[Resource][Method]>> {
...variables: Parameters<Commerce[Resource][Method] | any>
): Promise<ReturnType<Commerce[Resource][Method] | any>> {
//@ts-ignore
// Provider TODO: Fix types here.
const data = await commerce[resource][method](...variables)
return data
}

View File

@ -11,13 +11,12 @@ export default useLogin as UseLogin<typeof handler>
export const handler: MutationHook<LoginHook> = {
fetchOptions: {
url: '/api/login',
method: 'POST'
method: 'POST',
},
async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) {
throw new CommerceError({
message:
'An email and password are required to login',
message: 'An email and password are required to login',
})
}
@ -26,17 +25,19 @@ export const handler: MutationHook<LoginHook> = {
body: { email, password },
})
},
useHook: ({ fetch }) => () => {
const { revalidate } = useCustomer()
const {revalidate: revalidateCart} = useCart()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
await revalidateCart()
return data
},
[fetch, revalidate, revalidateCart]
)
},
useHook:
({ fetch }) =>
() => {
const { mutate } = useCustomer()
const { mutate: mutateCart } = useCart()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await mutate()
await mutateCart()
return data
},
[fetch, mutate, mutateCart]
)
},
}

View File

@ -29,16 +29,18 @@ export const handler: MutationHook<SignupHook> = {
body: { firstName, lastName, email, password },
})
},
useHook: ({ fetch }) => () => {
const { revalidate } = useCustomer()
useHook:
({ fetch }) =>
() => {
const { mutate } = useCustomer()
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await revalidate()
return data
},
[fetch, revalidate]
)
},
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await mutate()
return data
},
[fetch, mutate]
)
},
}

View File

@ -13,24 +13,26 @@ export const handler: MutationHook<AddItemHook> = {
url: '/api/wishlist',
method: 'POST',
},
useHook: ({ fetch }) => () => {
const { data: customer } = useCustomer()
const { revalidate } = useWishlist()
useHook:
({ fetch }) =>
() => {
const { data: customer } = useCustomer()
const { mutate } = useWishlist()
return useCallback(
async function addItem(item) {
if (!customer) {
// A signed customer is required in order to have a wishlist
throw new CommerceError({
message: 'Signed customer not found',
})
}
// TODO: add validations before doing the fetch
const data = await fetch({ input: { item } })
await revalidate()
return data
},
[fetch, revalidate, customer]
)
},
return useCallback(
async function addItem(item) {
if (!customer) {
// A signed customer is required in order to have a wishlist
throw new CommerceError({
message: 'Signed customer not found',
})
}
// TODO: add validations before doing the fetch
const data = await fetch({ input: { item } })
await mutate()
return data
},
[fetch, mutate, customer]
)
},
}

View File

@ -15,24 +15,26 @@ export const handler: MutationHook<RemoveItemHook> = {
url: '/api/wishlist',
method: 'DELETE',
},
useHook: ({ fetch }) => ({ wishlist } = {}) => {
const { data: customer } = useCustomer()
const { revalidate } = useWishlist(wishlist)
useHook:
({ fetch }) =>
({ wishlist } = {}) => {
const { data: customer } = useCustomer()
const { mutate } = useWishlist(wishlist)
return useCallback(
async function removeItem(input) {
if (!customer) {
// A signed customer is required in order to have a wishlist
throw new CommerceError({
message: 'Signed customer not found',
})
}
return useCallback(
async function removeItem(input) {
if (!customer) {
// A signed customer is required in order to have a wishlist
throw new CommerceError({
message: 'Signed customer not found',
})
}
const data = await fetch({ input: { itemId: String(input.id) } })
await revalidate()
return data
},
[fetch, revalidate, customer]
)
},
const data = await fetch({ input: { itemId: String(input.id) } })
await mutate()
return data
},
[fetch, mutate, customer]
)
},
}

View File

@ -49,15 +49,15 @@ export const handler: MutationHook<LoginHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -42,15 +42,15 @@ export const handler: MutationHook<SignupHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -49,15 +49,15 @@ export const handler: MutationHook<LoginHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -53,15 +53,15 @@ export const handler: MutationHook<SignupHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -23,7 +23,7 @@ An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Com
- They rely on [taxonomies'](https://dev-docs.spreecommerce.org/internals/products#taxons-and-taxonomies) permalinks in Spree.
- Go to the Spree admin panel and create `Categories` and `Brands` taxonomies if they don't exist and copy their permalinks into `.env.local` in NextJS Commerce.
1. Finally, run `yarn dev` :tada:
1. Finally, run `npm run dev` :tada:
[1]: https://spreecommerce.org/
[2]: https://github.com/vercel/commerce

View File

@ -71,9 +71,9 @@ export const handler: MutationHook<LoginHook> = {
async function login(input) {
const data = await fetch({ input })
await customer.revalidate()
await cart.revalidate()
await wishlist.revalidate()
await customer.mutate()
await cart.mutate()
await wishlist.mutate()
return data
},

View File

@ -81,9 +81,9 @@ export const handler: MutationHook<SignupHook> = {
async (input) => {
const data = await fetch({ input })
await customer.revalidate()
await cart.revalidate()
await wishlist.revalidate()
await customer.mutate()
await cart.mutate()
await wishlist.mutate()
return data
},

View File

@ -75,7 +75,7 @@ export const handler: MutationHook<ExplicitWishlistAddItemHook> = {
},
})
await wishlist.revalidate()
await wishlist.mutate()
return data
},

View File

@ -62,7 +62,7 @@ export const handler: MutationHook<ExplicitWishlistRemoveItemHook> = {
},
})
await wishlist.revalidate()
await wishlist.mutate()
return data
},

View File

@ -62,15 +62,15 @@ export const handler: MutationHook<LoginHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -47,15 +47,15 @@ export const handler: MutationHook<SignupHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -39,15 +39,15 @@ export const handler: MutationHook<LoginHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function login(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -57,15 +57,15 @@ export const handler: MutationHook<SignupHook> = {
useHook:
({ fetch }) =>
() => {
const { revalidate } = useCustomer()
const { mutate } = useCustomer()
return useCallback(
async function signup(input) {
const data = await fetch({ input })
await revalidate()
await mutate()
return data
},
[fetch, revalidate]
[fetch, mutate]
)
},
}

View File

@ -4,9 +4,7 @@ import useLogin from '@framework/auth/use-login'
import { useUI } from '@components/ui/context'
import { validate } from 'email-validator'
interface Props {}
const LoginView: FC<Props> = () => {
const LoginView: React.FC = () => {
// Form State
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
@ -35,8 +33,8 @@ const LoginView: FC<Props> = () => {
})
setLoading(false)
closeModal()
} catch ({ errors }) {
setMessage(errors[0].message)
} catch (e: any) {
setMessage(e.errors[0].message)
setLoading(false)
setDisabled(false)
}

View File

@ -15,7 +15,7 @@ import { useCheckoutContext } from '../context'
const CheckoutSidebarView: FC = () => {
const [loadingSubmit, setLoadingSubmit] = useState(false)
const { setSidebarView, closeSidebar } = useUI()
const { data: cartData, revalidate: refreshCart } = useCart()
const { data: cartData, mutate: refreshCart } = useCart()
const { data: checkoutData, submit: onCheckout } = useCheckout()
const { clearCheckoutFields } = useCheckoutContext()

View File

@ -1,7 +1,6 @@
.root {
@apply text-center p-6 bg-primary text-sm flex-row justify-center items-center font-medium fixed bottom-0 w-full z-30 transition-all duration-300 ease-out;
@screen md {
@apply flex text-left;
}
@apply text-center p-6 bg-primary text-sm flex-row
justify-center items-center font-medium fixed bottom-0
w-full z-30 transition-all duration-300 ease-out
md:flex md:text-left;
}

View File

@ -16,16 +16,6 @@
.dropdownMenu {
@apply fixed right-0 top-12 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full;
@screen lg {
@apply absolute border border-accent-1 shadow-lg w-56 h-auto;
}
}
.closeButton {
@screen md {
@apply hidden;
}
}
.item {
@ -44,3 +34,15 @@
.icon.active {
transform: rotate(180deg);
}
@screen lg {
.dropdownMenu {
@apply absolute border border-accent-1 shadow-lg w-56 h-auto;
}
}
@screen md {
.closeButton {
@apply hidden;
}
}

View File

@ -1,11 +1,13 @@
.dropdownMenu {
@apply fixed right-0 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full;
@screen lg {
@screen lg {
.dropdownMenu {
@apply absolute top-10 border border-accent-1 shadow-lg w-56 h-auto;
}
}
.dropdownMenu {
@apply fixed right-0 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full;
}
.link {
@apply text-primary flex cursor-pointer px-6 py-3 flex transition ease-in-out duration-150 leading-6 font-medium items-center;
text-transform: capitalize;

View File

@ -13,52 +13,50 @@ interface SwatchProps {
label?: string | null
}
const Swatch: React.FC<Omit<ButtonProps, 'variant'> & SwatchProps> = React.memo(
({
active,
className,
color = '',
label = null,
variant = 'size',
...props
}) => {
variant = variant?.toLowerCase()
const Swatch: React.FC<Omit<ButtonProps, 'variant'> & SwatchProps> = ({
active,
className,
color = '',
label = null,
variant = 'size',
...props
}) => {
variant = variant?.toLowerCase()
if (label) {
label = label?.toLowerCase()
}
const swatchClassName = cn(
s.swatch,
{
[s.color]: color,
[s.active]: active,
[s.size]: variant === 'size',
[s.dark]: color ? isDark(color) : false,
[s.textLabel]: !color && label && label.length > 3,
},
className
)
return (
<Button
role="option"
aria-selected={active}
aria-label={variant && label ? `${variant} ${label}` : 'Variant Swatch'}
className={swatchClassName}
{...(label && color && { title: label })}
style={color ? { backgroundColor: color } : {}}
{...props}
>
{color && active && (
<span>
<Check />
</span>
)}
{!color ? label : null}
</Button>
)
if (label) {
label = label?.toLowerCase()
}
)
export default Swatch
const swatchClassName = cn(
s.swatch,
{
[s.color]: color,
[s.active]: active,
[s.size]: variant === 'size',
[s.dark]: color ? isDark(color) : false,
[s.textLabel]: !color && label && label.length > 3,
},
className
)
return (
<Button
role="option"
aria-selected={active}
aria-label={variant && label ? `${variant} ${label}` : 'Variant Swatch'}
className={swatchClassName}
{...(label && color && { title: label })}
style={color ? { backgroundColor: color } : {}}
{...props}
>
{color && active && (
<span>
<Check />
</span>
)}
{!color ? label : null}
</Button>
)
}
export default React.memo(Swatch)

View File

@ -21,6 +21,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
disabled?: boolean
}
// eslint-disable-next-line react/display-name
const Button: React.FC<ButtonProps> = forwardRef((props, buttonRef) => {
const {
className,

View File

@ -10,7 +10,7 @@ export interface CollapseProps {
children: ReactNode
}
const Collapse: FC<CollapseProps> = React.memo(({ title, children }) => {
const Collapse: FC<CollapseProps> = ({ title, children }) => {
const [isActive, setActive] = useState(false)
const [ref, { height: viewHeight }] = useMeasure()
@ -41,6 +41,6 @@ const Collapse: FC<CollapseProps> = React.memo(({ title, children }) => {
</a.div>
</div>
)
})
}
export default Collapse
export default React.memo(Collapse)

View File

@ -1,53 +1,25 @@
.root {
@apply grid grid-cols-1 gap-0;
@screen lg {
@apply grid-cols-3 grid-rows-2;
}
& > * {
@apply row-span-1 bg-transparent box-border overflow-hidden;
height: 500px;
max-height: 800px;
@screen lg {
@apply col-span-1;
height: inherit;
}
}
}
.root > * {
@apply row-span-1 bg-transparent box-border overflow-hidden;
height: 500px;
max-height: 800px;
}
.default {
& > * {
@apply bg-transparent;
}
.default > * {
@apply bg-transparent;
}
.layoutNormal {
@apply gap-3;
}
@screen md {
.layoutNormal > * {
max-height: min-content !important;
}
}
@screen lg {
.layoutNormal > * {
max-height: 400px;
}
}
.layoutA {
& > *:nth-child(6n + 1),
& > *:nth-child(6n + 5) {
@apply row-span-2;
@apply row-span-2 lg:col-span-2;
height: var(--row-height);
@screen lg {
@apply col-span-2;
}
}
&.filled {
@ -72,12 +44,8 @@
.layoutB {
& > *:nth-child(6n + 2),
& > *:nth-child(6n + 4) {
@apply row-span-2;
@apply row-span-2 lg:col-span-2;
height: var(--row-height);
@screen lg {
@apply col-span-2;
}
}
&.filled {
@ -102,12 +70,8 @@
.layoutC {
& > *:nth-child(12n + 1),
& > *:nth-child(12n + 8) {
@apply row-span-2;
@apply row-span-2 lg:col-span-2;
height: var(--row-height);
@screen lg {
@apply col-span-2;
}
}
&.filled {
@ -130,12 +94,8 @@
.layoutD {
& > *:nth-child(12n + 2),
& > *:nth-child(12n + 7) {
@apply row-span-2;
@apply row-span-2 lg:col-span-2;
height: var(--row-height);
@screen lg {
@apply col-span-2;
}
}
&.filled {
@ -152,3 +112,24 @@
}
}
}
@screen md {
.layoutNormal > * {
max-height: min-content !important;
}
}
@screen lg {
.root {
@apply grid-cols-3 grid-rows-2;
}
.root > * {
@apply col-span-1;
height: inherit;
}
.layoutNormal > * {
max-height: 400px;
}
}

View File

@ -31,8 +31,8 @@ const WishlistButton: FC<Props> = ({
const itemInWishlist = data?.items?.find(
// @ts-ignore Wishlist is not always enabled
(item) =>
item.product_id === Number(productId) &&
(item.variant_id as any) === Number(variant.id)
item.product_id === productId &&
item.variant_id === variant.id
)
const handleWishlistChange = async (e: any) => {

View File

@ -48,6 +48,7 @@ export default function Home({
imgProps={{
width: i === 0 ? 1080 : 540,
height: i === 0 ? 1080 : 540,
priority: true,
}}
/>
))}

View File

@ -1,7 +1,8 @@
module.exports = {
plugins: [
'tailwindcss/nesting',
'tailwindcss',
'postcss-nesting',
'autoprefixer',
'postcss-flexbugs-fixes',
[
'postcss-preset-env',

View File

@ -1,19 +1,9 @@
module.exports = {
future: {
purgeLayersByDefault: true,
applyComplexClasses: true,
},
purge: {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
options: {
safelist: {
standard: ['outline-none'],
},
},
},
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
safelist: ['outline-none'],
theme: {
extend: {
maxWidth: {

View File

@ -15,6 +15,7 @@
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"useUnknownInCatchVariables": false,
"paths": {
"@lib/*": ["lib/*"],
"@utils/*": ["utils/*"],