diff --git a/cypress/integration/performance.test.js b/cypress/integration/performance.test.js index a015451b..92bf66b9 100644 --- a/cypress/integration/performance.test.js +++ b/cypress/integration/performance.test.js @@ -47,13 +47,13 @@ describe('React rendering performance', () => { } }) - it('Renders Alert component once', () => { - const mark = 'Alert' + // it('Renders Alert component once', () => { + // const mark = 'Alert' - for (const path of paths) { - testMark(path, mark) - } - }) + // for (const path of paths) { + // testMark(path, mark) + // } + // }) it('Renders Page component once', () => { const mark = 'Page' diff --git a/package.json b/package.json index 42e4d8f1..a842a632 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@faststore/api": "^1.6.26", "@faststore/sdk": "^1.6.26", "@faststore/ui": "^1.6.26", + "@reach/router": "^1.3.4", "@vtex/gatsby-plugin-nginx": "^1.6.26", "@vtex/gatsby-source-cms": "^0.2.4", "@vtex/gatsby-source-store": "^1.6.26", @@ -103,4 +104,4 @@ "stylelint --fix" ] } -} \ No newline at end of file +} diff --git a/src/Layout.tsx b/src/Layout.tsx index 31350bd7..b954f39b 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -1,30 +1,35 @@ -import React, { lazy, Suspense } from 'react' -import Alert from 'src/components/common/Alert' +import React, { lazy, Suspense, useEffect } from 'react' import Footer from 'src/components/common/Footer' import Navbar from 'src/components/common/Navbar' -import Toast from 'src/components/common/Toast' import { useUI } from 'src/sdk/ui' import type { PropsWithChildren } from 'react' +import { useLocation } from '@reach/router' const CartSidebar = lazy(() => import('src/components/cart/CartSidebar')) function Layout({ children }: PropsWithChildren) { const { displayMinicart } = useUI() + const { pathname } = useLocation() + + useEffect(() => { + if (pathname === '/') { + document.querySelector('body')?.classList.remove('notHome') + document.querySelector('body')?.classList.add('home') + } else { + document.querySelector('body')?.classList.remove('home') + document.querySelector('body')?.classList.add('notHome') + } + }) + return (
- - Get 10% off today: NEW10 - -
{children}
- - {displayMinicart && ( diff --git a/src/components/cart/CartItem/CartItem.tsx b/src/components/cart/CartItem/CartItem.tsx index 0482ad85..c3633576 100644 --- a/src/components/cart/CartItem/CartItem.tsx +++ b/src/components/cart/CartItem/CartItem.tsx @@ -1,7 +1,6 @@ import { Card, CardActions, CardContent, CardImage } from '@faststore/ui' import React from 'react' import Button from 'src/components/ui/Button' -import Icon from 'src/components/ui/Icon' import { Image } from 'src/components/ui/Image' import Price from 'src/components/ui/Price' import QuantitySelector from 'src/components/ui/QuantitySelector' @@ -30,8 +29,8 @@ function CartItem({ item }: Props) { {item.itemOffered.image[0].alternateName}
@@ -59,23 +58,17 @@ function CartItem({ item }: Props) { />
+ + + updateItemQuantity(item.id, quantity)} + /> + - - - - updateItemQuantity(item.id, quantity)} - /> - ) } diff --git a/src/components/cart/CartItem/cart-item.scss b/src/components/cart/CartItem/cart-item.scss index 166c9707..99d4a4de 100644 --- a/src/components/cart/CartItem/cart-item.scss +++ b/src/components/cart/CartItem/cart-item.scss @@ -1,16 +1,16 @@ @import "src/styles/scaffold"; .cart-item { - padding: var(--space-3); - background-color: var(--bg-neutral-lightest); - border: var(--border-width-0) solid var(--color-border-display); - border-radius: var(--border-radius-default); + padding: var(--space-4) var(--space-5) var(--space-4) var(--space-3); + border-bottom: var(--border-width-0) solid var(--color-gray-transparent); + + @include media("
-

Your Cart

+ } + /> +

Seu carrinho

{totalItems} @@ -45,14 +48,10 @@ function CartSidebar() { } + icon={} onClick={() => dismissTransition.current?.()} />
- }> - Free shipping starts at $300 - - {isEmpty ? ( dismissTransition.current?.()} /> ) : ( @@ -81,10 +80,16 @@ function CartSidebar() { iconPosition="right" {...btnProps} > - {isValidating ? 'Loading...' : 'Checkout'} + {isValidating ? 'Loading...' : 'finalizar o pedido'} } /> + } + onClick={() => dismissTransition.current?.()} + />
)} diff --git a/src/components/cart/CartSidebar/cart-sidebar.scss b/src/components/cart/CartSidebar/cart-sidebar.scss index 15ebee05..5a4afa7b 100644 --- a/src/components/cart/CartSidebar/cart-sidebar.scss +++ b/src/components/cart/CartSidebar/cart-sidebar.scss @@ -3,38 +3,81 @@ flex-direction: column; height: 100%; overflow: auto; - background-color: var(--bg-neutral-light); + background-color: rgb(255 255 255 / 80%); > header { display: flex; align-items: center; justify-content: space-between; - padding: var(--space-2) var(--page-padding-phone) var(--space-2); - background-color: var(--bg-neutral-lightest); + padding: var(--space-5) var(--page-padding-phone) var(--space-5); + color: var(--color-primary-4); + background-color: var(--color-white-transparent-9); + + @include media(" [data-store-icon-button] { &:last-of-type { margin-right: calc(-1 * var(--space-1)); + color: var(--color-primary-4); } } } + .empty-state[data-empty-state] { + button { + color: var(--color-theme-2); + cursor: pointer; + border-color: var(--color-theme-2); + } + } + .cart-sidebar__title { display: flex; align-items: center; + button { + color: var(--color-theme-3); + } + p { margin-right: var(--space-2); + font-size: var(--text-size-4); + font-weight: 500; line-height: 1.35; + color: var(--color-theme-3); + } + + [data-store-badge][data-store-badge="small"] { + display: flex; + align-items: center; + justify-content: center; + max-width: 27px; + padding: 2px 6px; + font-size: 12px; + font-weight: 400; + line-height: 15px; + background: #de68d1; + border-radius: 5px; + box-shadow: 0 1px 2px rgb(0 0 0 / 25%); + + &::after { + content: attr(data-items); + } } } > [data-store-list] { display: flex; flex-direction: column; - padding: var(--page-padding-phone); - overflow: auto; row-gap: var(--space-2); + padding: 0; + margin: 10px 7px 0 0; + overflow: auto; + &::-webkit-scrollbar { width: 10px; } + &::-webkit-scrollbar-track { background: rgb(255 255 255 / 20%); } + &::-webkit-scrollbar-thumb { background-color: #fbfbfb; } } > footer { @@ -45,5 +88,20 @@ [data-store-button] { width: 100%; } + + [aria-label="continuar comprando"] { + svg { + display: none; + } + + &::after { + display: block; + margin: 0 auto 22px; + font-size: 14px; + line-height: 17px; + color: var(--color-primary-4); + content: "continuar comprando"; + } + } } } diff --git a/src/components/cart/CartToggle/CartToggle.tsx b/src/components/cart/CartToggle/CartToggle.tsx index bef7132c..9e1b6788 100644 --- a/src/components/cart/CartToggle/CartToggle.tsx +++ b/src/components/cart/CartToggle/CartToggle.tsx @@ -11,7 +11,7 @@ function CartToggle() { {...btnProps} className="cart-toggle" aria-label={`Cart with ${btnProps['data-items']} items`} - icon={} + icon={} /> ) } diff --git a/src/components/cart/CartToggle/cart-toggle.scss b/src/components/cart/CartToggle/cart-toggle.scss index bbc3f605..fdfd4256 100644 --- a/src/components/cart/CartToggle/cart-toggle.scss +++ b/src/components/cart/CartToggle/cart-toggle.scss @@ -2,24 +2,33 @@ .cart-toggle[data-store-icon-button] { position: relative; + padding: var(--space-2); + margin-left: var(--space-3); + color: var(--color-neutral-0); + cursor: pointer; + border: 2px solid var(--color-neutral-0); + border-radius: var(--border-radius-circle); &::after { --cart-toggle-size: var(--space-3); position: absolute; - top: rem(6px); - left: rem(26px); + top: -9px; + right: -3px; display: flex; align-items: center; justify-content: center; - min-width: var(--cart-toggle-size); - height: var(--cart-toggle-size); - padding: var(--space-0); - color: var(--color-text-inverse); - font-weight: var(--text-weight-bold); - font-size: var(--text-size-0); - background: var(--bg-secondary-default); - border-radius: var(--border-radius-pill); + padding: 2px 6px; + font-size: 12px; + font-weight: 400; + line-height: 15px; content: attr(data-items); + background: #de68d1; + border-radius: 5px; + box-shadow: 0 1px 2px rgb(0 0 0 / 25%); + } + + &:hover { + background-color: transparent; } } diff --git a/src/components/cart/OrderSummary/OrderSummary.tsx b/src/components/cart/OrderSummary/OrderSummary.tsx index 2bc948f0..3f9f35db 100644 --- a/src/components/cart/OrderSummary/OrderSummary.tsx +++ b/src/components/cart/OrderSummary/OrderSummary.tsx @@ -22,12 +22,12 @@ function OrderSummary({ return (
  • - Subtotal ({numberOfItems} products) + Sub-total ({numberOfItems} produto) {useFormattedPrice(subTotal)}
  • {discount > 0 && (
  • - Discount + Desconto -{formattedDiscount}
  • )} diff --git a/src/components/cart/OrderSummary/order-summary.scss b/src/components/cart/OrderSummary/order-summary.scss index b8fd831f..7d42661c 100644 --- a/src/components/cart/OrderSummary/order-summary.scss +++ b/src/components/cart/OrderSummary/order-summary.scss @@ -6,10 +6,39 @@ justify-content: space-between; line-height: 1.5; - &[data-order-summary-discount] { color: var(--color-text-success); } + &[data-order-summary-discount] { + margin-bottom: 6px; + border-bottom: 1px solid var(--color-gray-transparent); + + span { + color: #4bb8db; + } + } + + &.title-subsection { + span:last-child { + font-size: 24px; + font-weight: 700; + color: #8586db; + } + } + + span { + font-size: 15px; + font-weight: 500; + line-height: 30px; + color: #333056; + } &:last-of-type { - margin-bottom: var(--space-2); + margin-bottom: var(--space-4); + + span { + font-size: 24px; + line-height: 30px; + color: #333056; + letter-spacing: 0.01em; + } } } } diff --git a/src/components/common/CarouselShelf/Arrows.tsx b/src/components/common/CarouselShelf/Arrows.tsx new file mode 100644 index 00000000..f76a0084 --- /dev/null +++ b/src/components/common/CarouselShelf/Arrows.tsx @@ -0,0 +1,58 @@ +import React from 'react' + +interface IconProps { + size?: { + width: number + height: number + } + viewBox?: string + color?: string +} + +export const LeftArrowIcon = ({ + size = { width: 25, height: 25 }, + viewBox = '0 0 16 16', + color = 'currentColor', +}: IconProps) => ( + + + +) + +export const RightArrowIcon = ({ + size = { width: 25, height: 25 }, + viewBox = '0 0 16 16', + color = 'currentColor', +}: IconProps) => ( + + + +) diff --git a/src/components/common/CarouselShelf/Bullets.tsx b/src/components/common/CarouselShelf/Bullets.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/common/CarouselShelf/CarouselShelf.tsx b/src/components/common/CarouselShelf/CarouselShelf.tsx new file mode 100644 index 00000000..a8826423 --- /dev/null +++ b/src/components/common/CarouselShelf/CarouselShelf.tsx @@ -0,0 +1,258 @@ +import type { KeyboardEvent, PropsWithChildren } from 'react' +import React, { useMemo } from 'react' +import type { SwipeableProps } from 'react-swipeable' +import IconButton from 'src/components/ui/IconButton' +import { Bullets } from '@faststore/ui' + +import { RightArrowIcon, LeftArrowIcon } from './Arrows' +import useSlider from './hooks/useSlider' +import useSlideVisibility from './hooks/useSlideVisibility' + +const createTransformValues = (infinite: boolean, totalItems: number) => { + const transformMap: Record = {} + const slideWidth = 100 / totalItems + + for (let idx = 0; idx < totalItems; ++idx) { + const currIdx = infinite ? idx - 1 : idx + const transformValue = -(slideWidth * idx) + + transformMap[currIdx] = transformValue + } + + return transformMap +} + +export interface CarouselProps extends SwipeableProps { + id?: string + testId?: string + infiniteMode?: boolean + controls?: 'complete' | 'navigationArrows' | 'paginationBullets' + transition?: { + duration: number + property: string + delay?: number + timing?: string + } + itensPerPageSlider?: number +} + +function CarouselShelf({ + infiniteMode = true, + controls = 'complete', + testId = 'store-carousel', + transition = { + duration: 400, + property: 'transform', + }, + children, + id = 'store-carousel', + itensPerPageSlider = 1, + ...swipeableConfigOverrides +}: PropsWithChildren) { + const childrenArray = React.Children.toArray(children) + const childrenCount = childrenArray.length + const numberOfSlides = infiniteMode ? childrenCount + 2 : childrenCount + const slidingTransition = `${transition.property} ${transition.duration}ms ${ + transition.timing ?? '' + } ${transition.delay ?? ''}` + + const showNavigationArrows = + controls === 'complete' || controls === 'navigationArrows' + + const showPaginationBullets = + controls === 'complete' || controls === 'paginationBullets' + + const transformValues = useMemo( + () => createTransformValues(infiniteMode, numberOfSlides), + [numberOfSlides, infiniteMode] + ) + + const { handlers, slide, sliderState, sliderDispatch } = useSlider({ + totalItems: childrenCount, + itemsPerPage: itensPerPageSlider, + infiniteMode, + ...swipeableConfigOverrides, + }) + + const { isItemVisible, shouldRenderItem } = useSlideVisibility({ + itemsPerPage: sliderState.itemsPerPage, + currentSlide: sliderState.currentItem, + totalItems: childrenCount, + }) + + const postRenderedSlides = + infiniteMode && children ? childrenArray.slice(0, 1) : [] + + const preRenderedSlides = + infiniteMode && children ? childrenArray.slice(childrenCount - 1) : [] + + const slides = preRenderedSlides.concat(children ?? [], postRenderedSlides) + + const slidePrevious = () => { + if ( + sliderState.sliding || + (!infiniteMode && sliderState.currentPage === 0) + ) { + return + } + + slide('previous', sliderDispatch) + } + + const slideNext = () => { + if ( + sliderState.sliding || + (!infiniteMode && sliderState.currentPage === childrenCount - 1) + ) { + return + } + + slide('next', sliderDispatch) + } + + // accessible behavior for tablist + const handleBulletsKeyDown = (event: KeyboardEvent) => { + switch (event.key) { + case 'ArrowLeft': { + slidePrevious() + break + } + + case 'ArrowRight': { + slideNext() + break + } + + case 'Home': { + slide(0, sliderDispatch) + break + } + + case 'End': { + slide(childrenCount - 1, sliderDispatch) + break + } + + default: + } + } + + return ( +
    +
    + {/* TODO: arrumar essa porra - width: `${numberOfSlides * (100 / itensPerPageSlider)}%`, */} +
    { + sliderDispatch({ + type: 'STOP_SLIDE', + }) + + if (sliderState.currentItem >= childrenCount) { + sliderDispatch({ + type: 'GO_TO_PAGE', + payload: { + pageIndex: 0, + shouldSlide: false, + }, + }) + } + + if (sliderState.currentItem < 0) { + sliderDispatch({ + type: 'GO_TO_PAGE', + payload: { + pageIndex: sliderState.totalPages - 1, + shouldSlide: false, + }, + }) + } + }} + aria-live="polite" + > + {slides.map((currentSlide, idx) => ( +
    + {shouldRenderItem(idx - Number(infiniteMode)) + ? currentSlide + : null} +
    + ))} +
    +
    + + {showNavigationArrows && ( +
    + } + /> + } + /> +
    + )} + + {showPaginationBullets && ( +
    + { + if (sliderState.sliding) { + return + } + + slide(idx, sliderDispatch) + }} + ariaControlsGenerator={(idx) => + `carousel-item-${idx}-bullets-${id}` + } + onKeyDown={handleBulletsKeyDown} + onFocus={(event) => { + event.currentTarget.focus() + }} + /> +
    + )} +
    + ) +} + +export default CarouselShelf diff --git a/src/components/common/CarouselShelf/IconButton.tsx b/src/components/common/CarouselShelf/IconButton.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/common/CarouselShelf/hooks/useSlideVisibility.ts b/src/components/common/CarouselShelf/hooks/useSlideVisibility.ts new file mode 100644 index 00000000..610d72e3 --- /dev/null +++ b/src/components/common/CarouselShelf/hooks/useSlideVisibility.ts @@ -0,0 +1,59 @@ +import { useRef, useEffect } from 'react' + +export interface UseSlideVisibilityArgs { + currentSlide: number + itemsPerPage: number + totalItems: number +} + +interface IsSlideVisibleArgs { + itemsPerPage: number + currentSlide: number + slideIdx: number + totalItems: number +} + +function isSlideVisible({ + itemsPerPage, + currentSlide, + slideIdx, + totalItems, +}: IsSlideVisibleArgs) { + const isClonedSlide = currentSlide < 0 || currentSlide >= totalItems + const isVisible = + slideIdx >= currentSlide && slideIdx < currentSlide + itemsPerPage + + return isClonedSlide || isVisible +} + +export default function useSlideVisibility({ + currentSlide, + itemsPerPage, + totalItems, +}: UseSlideVisibilityArgs) { + /** Keeps track of slides that have been visualized before. + * We want to keep rendering them because the issue is mostly rendering + * slides that might never be viewed; On the other hand, hiding slides + * that were visible causes visual glitches */ + const visitedSlides = useRef>(new Set()) + + useEffect(() => { + for (let i = 0; i < itemsPerPage; i++) { + visitedSlides.current.add(currentSlide + i) + } + }, [currentSlide, itemsPerPage]) + + const isItemVisible = (index: number) => + isSlideVisible({ + slideIdx: index, + currentSlide, + itemsPerPage, + totalItems, + }) + + const shouldRenderItem = (index: number) => { + return visitedSlides.current.has(index) || isItemVisible(index) + } + + return { shouldRenderItem, isItemVisible } +} diff --git a/src/components/common/CarouselShelf/hooks/useSlider.ts b/src/components/common/CarouselShelf/hooks/useSlider.ts new file mode 100644 index 00000000..1c888387 --- /dev/null +++ b/src/components/common/CarouselShelf/hooks/useSlider.ts @@ -0,0 +1,210 @@ +import type { Dispatch } from 'react' +import { useReducer } from 'react' +import type { SwipeableProps } from 'react-swipeable' +import { useSwipeable } from 'react-swipeable' + +export type SlideDirection = 'next' | 'previous' + +interface NextPageAction { + type: 'NEXT_PAGE' +} + +interface PreviousPageAction { + type: 'PREVIOUS_PAGE' +} + +interface GoToPageAction { + type: 'GO_TO_PAGE' + payload: { + pageIndex: number + shouldSlide: boolean + } +} + +interface StopSlideAction { + type: 'STOP_SLIDE' +} + +export type Action = + | NextPageAction + | PreviousPageAction + | StopSlideAction + | GoToPageAction + +export type SliderDispatch = Dispatch + +export interface SliderState { + /** + * The `currentItem` in a Slider with multiple items in a single page is + * always **the one with the lowest index** in the current page. + */ + currentItem: number + /** Currently active page */ + currentPage: number + /** + * Whether or not the Slider is currently sliding. This is useful to + * manipulate the `transition` property in a component. + */ + sliding: boolean + /** The direction in which the Slider is sliding. */ + slideDirection: SlideDirection + /** The total number of unique items in the slider. */ + totalItems: number + /** The number of items in a single page. */ + itemsPerPage: number + /** The total number of pages in the slider. */ + totalPages: number + /** Whether or not the slider is infinite. */ + infinite: boolean +} + +export const nextPage = (current: number, total: number) => + (current + 1) % total + +export const previousPage = (current: number, total: number) => + (total - ((total - current + 1) % total)) % total + +function reducer(state: SliderState, action: Action): SliderState { + switch (action.type) { + case 'NEXT_PAGE': { + // If `state.infinite` is true, we need to take into account an extra + // page in the calculation. This extra page is a clone of the first page. + const adjustedTotalPages = state.infinite + ? state.totalPages + 1 + : state.totalPages + + const nextPageIndex = nextPage(state.currentPage, adjustedTotalPages) + + const nextItemIndex = + (nextPageIndex % adjustedTotalPages) * state.itemsPerPage + + return { + ...state, + sliding: true, + slideDirection: 'next', + currentItem: nextItemIndex, + currentPage: nextPageIndex, + } + } + + case 'PREVIOUS_PAGE': { + // If `state.infinite` is true, we need to take into account an extra + // page in the calculation. This extra page is a clone of the first page. + const adjustedTotalPages = state.infinite + ? state.totalPages + 1 + : state.totalPages + + // If `state.infinite` is true and we're currently on page 0, we need to + // let the slider go to page -1. This -1 page is a clone of the last page. + const shouldGoToClone = state.infinite && state.currentPage === 0 + const previousPageIndex = shouldGoToClone + ? -1 + : previousPage(state.currentPage, state.totalPages) + + return { + ...state, + sliding: true, + slideDirection: 'previous', + currentItem: + (previousPageIndex % adjustedTotalPages) * state.itemsPerPage, + currentPage: previousPageIndex, + } + } + + case 'GO_TO_PAGE': { + if (action.payload.pageIndex === state.currentPage) { + return state + } + + return { + ...state, + sliding: action.payload.shouldSlide, + slideDirection: + action.payload.pageIndex > state.currentPage ? 'next' : 'previous', + currentItem: + (action.payload.pageIndex % state.totalPages) * state.itemsPerPage, + currentPage: action.payload.pageIndex, + } + } + + case 'STOP_SLIDE': + return { ...state, sliding: false } + + default: + return state + } +} + +const defaultSliderState = ( + totalItems: number, + itemsPerPage: number, + infinite: boolean +): SliderState => ({ + currentItem: 0, + currentPage: 0, + sliding: false, + slideDirection: 'next', + totalItems, + itemsPerPage, + totalPages: Math.ceil(totalItems / itemsPerPage), + infinite, +}) + +const slide = (page: SlideDirection | number, dispatch: Dispatch) => { + if (page === 'next') { + dispatch({ + type: 'NEXT_PAGE', + }) + } + + if (page === 'previous') { + dispatch({ + type: 'PREVIOUS_PAGE', + }) + } + + if (typeof page === 'number') { + dispatch({ + type: 'GO_TO_PAGE', + payload: { + pageIndex: page, + shouldSlide: true, + }, + }) + } +} + +export interface UseSliderArgs extends SwipeableProps { + /** The total number of unique items in the slider. */ + totalItems: number + /** The number of items in a single slider page. */ + itemsPerPage?: number + /** Whether or not the slider is infinite. */ + infiniteMode?: boolean +} + +export default function useSlider({ + totalItems, + itemsPerPage = 1, + infiniteMode = false, + ...swipeableConfigOverrides +}: UseSliderArgs) { + const [sliderState, sliderDispatch] = useReducer(reducer, undefined, () => + defaultSliderState(totalItems, itemsPerPage, infiniteMode) + ) + + const handlers = useSwipeable({ + onSwipedRight: () => slide('previous', sliderDispatch), + onSwipedLeft: () => slide('next', sliderDispatch), + preventDefaultTouchmoveEvent: true, + trackMouse: true, + ...swipeableConfigOverrides, + }) + + return { + handlers, + slide, + sliderState, + sliderDispatch, + } +} diff --git a/src/components/common/CarouselShelf/index.ts b/src/components/common/CarouselShelf/index.ts new file mode 100644 index 00000000..806d2b8d --- /dev/null +++ b/src/components/common/CarouselShelf/index.ts @@ -0,0 +1,2 @@ +export { default } from './CarouselShelf' +export * from './CarouselShelf' diff --git a/src/components/common/Footer/Footer.tsx b/src/components/common/Footer/Footer.tsx index 7614fe35..eb5a124b 100644 --- a/src/components/common/Footer/Footer.tsx +++ b/src/components/common/Footer/Footer.tsx @@ -21,7 +21,7 @@ function Footer() {
    -

    Follow us

    +

    Siga-nos

  • } /> + {/* */} -

    Payment Methods

    +

    Bandeiras

  • @@ -137,13 +140,26 @@ function Footer() {
    -

    This website uses VTEX technology

    - In-store price may vary. Prices and offers are subject to change. - 2021 Store name. All rights reserved. Store is a trademark of Store - and its affiliated companies. + Powered by + + } + /> +

    +

    + 2021 FRN³ - Todos os direitos reservados :D
    + CNPJ 123456789789456123 - Telefone: 123456789789456132

    -
    Mount St, 000, New York / NY - 00000.
    +
    Rua dos Nerds 404
  • diff --git a/src/components/common/Footer/FooterLinks.tsx b/src/components/common/Footer/FooterLinks.tsx index e851af25..5a7bcf29 100644 --- a/src/components/common/Footer/FooterLinks.tsx +++ b/src/components/common/Footer/FooterLinks.tsx @@ -5,43 +5,43 @@ import Accordion, { AccordionItem } from 'src/components/ui/Accordion' const links = [ { - title: 'Our company', + title: 'Empresa', items: [ { - href: '/', - name: 'About Us', + href: '/about', + name: 'Quem somos', }, { - href: '/', - name: 'Our Blog', + href: '/about', + name: 'Blog', }, { - href: '/', - name: 'Stores', + href: '/about', + name: 'Nossas lojas', }, { - href: '/', - name: 'Work With Us', + href: '/about', + name: 'Trabalhe conosco', }, ], }, { - title: 'Orders & Purchases', + title: 'Minha conta', items: [ { - href: '/', - name: 'Check Order Status', + href: '/about', + name: 'Meus pedidos', }, { - href: '/', - name: 'Returns and Exchanges', + href: '/about', + name: 'Alterações de pedido', }, { - href: '/', - name: 'Product Recall', + href: '/about', + name: 'Recall', }, { - href: '/', + href: '/about', name: 'Gift Cards', }, ], @@ -50,32 +50,32 @@ const links = [ title: 'Support & Services', items: [ { - href: '/', - name: 'Support Center', + href: '/about', + name: 'Suporte', }, { - href: '/', - name: 'Schedule a Service', + href: '/about', + name: 'Agende um serviço', }, { - href: '/', - name: 'Contact Us', + href: '/about', + name: 'Contato', }, ], }, { - title: 'Partnerships', + title: 'Parcerias', items: [ { - href: '/', - name: 'Affiliate Program', + href: '/about', + name: 'Afiliados', }, { - href: '/', - name: 'Advertise with US', + href: '/about', + name: 'FRN no mundo', }, { - href: '/', + href: '/about', name: 'Market Place', }, ], diff --git a/src/components/common/Footer/footer.scss b/src/components/common/Footer/footer.scss index ffcbe5b4..4be531be 100644 --- a/src/components/common/Footer/footer.scss +++ b/src/components/common/Footer/footer.scss @@ -1,10 +1,15 @@ @import "src/styles/scaffold"; .footer { - background-color: var(--bg-neutral-light); + background-color: var(--bg-neutral-lightest); .incentives { padding-top: 0; + + @include media(">=notebook") { + width: fit-content; + margin: 0 auto; + } } content-visibility: auto; @@ -32,8 +37,8 @@ &::before { width: 100%; - border-top: 1px solid var(--color-border-display); content: ""; + border-top: 1px solid rgb(51 48 86 / 10%); @include media(">=notebook") { grid-column: span 12; @@ -41,6 +46,10 @@ } } +.footer__logo { + margin: -33px 0 0 -35px; +} + .footer__section { @include media(">=notebook") { row-gap: var(--space-7); @@ -63,15 +72,15 @@ } @include media(">=notebook") { - padding-top: 0; row-gap: var(--grid-gap-2); + padding-top: 0; } [data-store-payment-methods] { margin: var(--space-4) auto; @include media(">=notebook") { - grid-column: 11 / span 2; + grid-column: 10 / span 2; order: 3; margin: 0; } @@ -80,6 +89,7 @@ [data-store-icon] { @include media(">=notebook") { grid-column: span 2; + margin: 6px; } } @@ -101,8 +111,8 @@ [data-store-list] { display: grid; grid-template-columns: repeat(4, minmax(0, max-content)); - justify-content: space-between; row-gap: var(--space-1); + justify-content: space-between; @include media("=notebook") { height: 54px; } + + p { margin: var(--space-2) auto; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; } address { + font-size: 12px; font-style: normal; + font-weight: 400; + line-height: 18px; } @include media(">=notebook") { @@ -174,6 +196,15 @@ .footer__links { [data-store-list] { margin-bottom: var(--space-2); + + a { + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + color: #333056; + letter-spacing: 0.01em; + } } [data-store-link] { @@ -200,10 +231,16 @@ .footer__links-columns { display: grid; - grid-gap: var(--grid-gap-1); grid-template-columns: repeat(4, 1fr); + grid-gap: var(--grid-gap-1); .title-sub-subsection { margin-bottom: var(--space-1); + font-size: 16px; + font-style: normal; + font-weight: 700; + line-height: 24px; + color: #333056; + letter-spacing: 0.01em; } } diff --git a/src/components/common/Navbar/Navbar.tsx b/src/components/common/Navbar/Navbar.tsx index 6596607a..29e905da 100644 --- a/src/components/common/Navbar/Navbar.tsx +++ b/src/components/common/Navbar/Navbar.tsx @@ -6,6 +6,7 @@ import SearchInput from 'src/components/common/SearchInput' import Icon from 'src/components/ui/Icon' import IconButton from 'src/components/ui/IconButton' import Link from 'src/components/ui/Link' +import IncentivesHeader from 'src/components/sections/Incentives/IncentivesHeader' import Logo from 'src/components/ui/Logo' import SignInLink from 'src/components/ui/SignInLink' import SlideOver from 'src/components/ui/SlideOver' @@ -25,7 +26,7 @@ function NavLinks({ onClickLink }: NavLinksProps) { allStoreCollection: { edges: links }, } = useStaticQuery(graphql` query StoreCollection { - allStoreCollection(filter: { type: { eq: Department } }) { + allStoreCollection(filter: { type: { eq: Category } }) { edges { node { slug @@ -39,8 +40,8 @@ function NavLinks({ onClickLink }: NavLinksProps) { `) return ( -