Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
hiagolcm committed Aug 21, 2024
1 parent 30ea719 commit 8a10562
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 42 deletions.
18 changes: 13 additions & 5 deletions react/components/ShippingOptionButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import { useCssHandles } from 'vtex.css-handles'
import { Spinner } from 'vtex.styleguide'

import TruckIcon from './TruckIcon'
import '../styles.css'
Expand All @@ -12,21 +13,28 @@ const CSS_HANDLES = [

interface Props {
onClick: () => void
loading: boolean
zipCode?: string
}

const ShippingOptionButton = ({ onClick, zipCode }: Props) => {
const ShippingOptionButton = ({ onClick, zipCode, loading }: Props) => {
const handles = useCssHandles(CSS_HANDLES)

return (
<button
onClick={onClick}
className={`${handles.buttonWrapper} bn flex justify-between items-center pointer pa0`}
className={`${handles.buttonWrapper} bn bg-transparent flex justify-between items-center pointer pa0`}
>
<TruckIcon className={handles.zipCodeIcon} />
<span className={`${handles.zipCodeButtonText} f6 rebel-pink fw5`}>
{zipCode ?? 'Enter a zip code'}
</span>
{loading ? (
<div className="ml4">
<Spinner size={14} />
</div>
) : (
<span className={`${handles.zipCodeButtonText} f6 rebel-pink fw5`}>
{zipCode ?? 'Enter a zip code'}
</span>
)}
</button>
)
}
Expand Down
73 changes: 55 additions & 18 deletions react/components/ShippingOptionDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React, { useState } from 'react'
import { IconClose, Input } from 'vtex.styleguide'
import { IconClose, Input, Spinner } from 'vtex.styleguide'

import styles from '../styles.css'

Expand All @@ -22,12 +22,24 @@ const Overlay = ({ open, onClose }: OverlayProps) => (
interface DrawerProps {
open: boolean
onClose: () => void
onSubmit: (zipCode: string) => void
isLoading: boolean
onSubmit: (zipCode?: string) => void
inputErrorMessage?: string
}

const Drawer = ({ open, onClose, onSubmit }: DrawerProps) => {
const Drawer = ({
open,
onClose,
onSubmit,
inputErrorMessage,
isLoading,
}: DrawerProps) => {
const [zipCode, setZipCode] = useState<string>()

const onSubmitForm = () => {
onSubmit(zipCode)
}

return (
<div
className={`${open ? styles['drawer--open'] : styles['drawer--close']} ${
Expand All @@ -51,37 +63,62 @@ const Drawer = ({ open, onClose, onSubmit }: DrawerProps) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setZipCode(e.target.value)
}}
type="text"
size="large"
placeholder="Zip code"
onKeyDown={(e: { key: string }) => {
if (e.key === 'Enter') {
onSubmitForm()
}
}}
error={inputErrorMessage}
errorMessage={inputErrorMessage}
autocomplete="off"
/>
</div>
<button
onClick={() => {
if (zipCode) {
onSubmit(zipCode)
onClose()
}
}}
className="bn w-100 bg-rebel-pink br-pill f6 white pa5 fw6 pointer"
type="submit"
>
Update
</button>

{isLoading ? (
<div className="flex justify-center items-center w-100">
<Spinner size={20} />
</div>
) : (
<button
onClick={onSubmitForm}
className="bn w-100 bg-rebel-pink br-pill f6 white pa5 fw6 pointer"
type="submit"
>
Update
</button>
)}
</div>
)
}

interface Props {
open: boolean
onClose: () => void
onSubmit: (zipCode: string) => void
onSubmit: (zipCode?: string) => void
isLoading: boolean
inputErrorMessage?: string
}

const ShippingOptionDrawer = ({ open, onClose, onSubmit }: Props) => {
const ShippingOptionDrawer = ({
open,
onClose,
onSubmit,
inputErrorMessage,
isLoading,
}: Props) => {
return (
<>
<Overlay open={open} onClose={onClose} />
<Drawer open={open} onClose={onClose} onSubmit={onSubmit} />
<Drawer
open={open}
onClose={onClose}
onSubmit={onSubmit}
inputErrorMessage={inputErrorMessage}
isLoading={isLoading}
/>
</>
)
}
Expand Down
64 changes: 56 additions & 8 deletions react/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
/* eslint-disable no-restricted-globals */
import React, { useEffect, useState } from 'react'
import { createPortal } from 'react-dom'
import { useSSR } from 'vtex.render-runtime'
import { useSSR, useRuntime } from 'vtex.render-runtime'

import ShippingOptionButton from './components/ShippingOptionButton'
import ShippingOptionDrawer from './components/ShippingOptionDrawer'
import { getCookie, setCookie } from './utils/cookie'

const SHIPPING_ZIPCODE_COOKIE = 'shipping-zipcode'
import { getCountryCode, getZipCode } from './utils/cookie'

function ShippingOptionZipCode() {
const body = window?.document?.body
const isSSR = useSSR()
const { account } = useRuntime()
const shouldCreatePortal = !isSSR && !!body
const [open, setOpen] = useState(false)
const [zipCode, setZipCode] = useState<string>()
const [isLoading, setIsLoading] = useState(true)
const [countryCode, setCountryCode] = useState<string>()
const [inputErrorMessage, setInputErrorMessage] = useState<string>()

useEffect(() => {
if (isSSR) {
return
}

setZipCode(getCookie(SHIPPING_ZIPCODE_COOKIE))
setZipCode(getZipCode())
setIsLoading(false)
setCountryCode(getCountryCode)
}, [isSSR])

if (shouldCreatePortal) {
Expand All @@ -36,21 +40,65 @@ function ShippingOptionZipCode() {
setOpen(false)
}

const onSubmit = (submittedZipCode: string) => {
const onError = (message: string) => {
setInputErrorMessage(message)
setIsLoading(false)

setTimeout(() => {
setInputErrorMessage(undefined)
}, 3000)
}

const onSubmit = async (submittedZipCode?: string) => {
if (!submittedZipCode) {
onError('Please enter your zipcode')

return
}

setZipCode(submittedZipCode)
setCookie(SHIPPING_ZIPCODE_COOKIE, submittedZipCode)
setIsLoading(true)

const postalCodeCall = await fetch(
`/api/checkout/pub/postal-code/${countryCode}/${submittedZipCode}?an=${account}`
)

const { geoCoordinates } = await postalCodeCall.json()

if (geoCoordinates.length === 0) {
onError('There are no deliveries for this region')

return
}

await fetch('/api/sessions', {
method: 'POST',
body: `{"public":{"facets":{"value":"zip-code=${submittedZipCode};coordinates=${geoCoordinates.join(
','
)}"}}}`,
headers: {
'Content-Type': 'application/json',
},
})

location.reload()
}

return (
<>
<ShippingOptionButton onClick={onOpen} zipCode={zipCode} />
<ShippingOptionButton
onClick={onOpen}
zipCode={zipCode}
loading={isLoading}
/>
{shouldCreatePortal
? createPortal(
<ShippingOptionDrawer
open={open}
onClose={onClose}
onSubmit={onSubmit}
inputErrorMessage={inputErrorMessage}
isLoading={isLoading}
/>,
body
)
Expand Down
1 change: 1 addition & 0 deletions react/typings/vtex.render-runtime.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
declare module 'vtex.render-runtime' {
export const useSSR
export const useRuntime
}
1 change: 1 addition & 0 deletions react/typings/vtex.styleguide.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
declare module 'vtex.styleguide' {
export const IconClose
export const Input
export const Spinner
}
53 changes: 42 additions & 11 deletions react/utils/cookie.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
export function setCookie(name: string, val: string) {
const date = new Date()
const value = val

// Set it expire in 7 days
date.setTime(date.getTime() + 1 * 24 * 60 * 60 * 1000)

// Set it
document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`
}

export function getCookie(name: string) {
const value = `; ${document.cookie}`
const parts = value.split(`; ${name}=`)
Expand All @@ -19,3 +8,45 @@ export function getCookie(name: string) {

return undefined
}

export function getZipCode() {
const segment = (window as any)?.__RUNTIME__.segmentToken

if (!segment) {
return
}

const { facets } = JSON.parse(atob(segment))

if (!facets) {
return
}

const zipCodeFacet = facets
.split(';')
.find((facet: string) => facet.indexOf('zip=code'))

if (!zipCodeFacet) {
return
}

const [, zipCode] = zipCodeFacet.split('=')

if (zipCode && zipCode[zipCode.length - 1] === ';') {
return zipCode.substring(0, zipCode.length - 1)
}

return zipCode
}

export function getCountryCode() {
const segment = (window as any)?.__RUNTIME__.segmentToken

if (!segment) {
return
}

const { countryCode } = JSON.parse(atob(segment))

return countryCode
}

0 comments on commit 8a10562

Please sign in to comment.