diff --git a/@generated/graphql/index.ts b/@generated/graphql/index.ts index ab3fdfc1..01f141f3 100644 --- a/@generated/graphql/index.ts +++ b/@generated/graphql/index.ts @@ -707,6 +707,14 @@ export type FloatQueryOperatorInput = { nin: InputMaybe>> } +/** Person data input to the newsletter. */ +export type IPersonNewsletter = { + /** Person's email. */ + email: Scalars['String'] + /** Person's name. */ + name: Scalars['String'] +} + /** Shopping cart input. */ export type IStoreCart = { /** Order information, including `orderNumber` and `acceptedOffer`. */ @@ -858,12 +866,18 @@ export type JsonQueryOperatorInput = { } export type Mutation = { + /** Subscribes a new person to the newsletter list. */ + subscribeToNewsletter: Maybe /** Checks for changes between the cart presented in the UI and the cart stored in the ecommerce platform. If changes are detected, it returns the cart stored on the platform. Otherwise, it returns `null`. */ validateCart: Maybe /** Updates a web session with the specified values. */ validateSession: Maybe } +export type MutationSubscribeToNewsletterArgs = { + data: IPersonNewsletter +} + export type MutationValidateCartArgs = { cart: IStoreCart } @@ -902,6 +916,12 @@ export type PageInfo = { totalCount: Scalars['Int'] } +/** Newsletter information. */ +export type PersonNewsletter = { + /** Person's ID in the newsletter list. */ + id: Scalars['String'] +} + export type Query = { /** Returns information about all collections. */ allCollections: StoreCollectionConnection @@ -2363,7 +2383,7 @@ export type StoreCollectionMeta = { selectedFacets: Array } -/** Product collection type. Possible values are `Department`, `Category`, `Brand` or `Cluster`. */ +/** Product collection type. Possible values are `Department`, `Category`, `Brand`, `Cluster`, `SubCategory` or `Collection`. */ export type StoreCollectionType = /** Product brand. */ | 'Brand' @@ -2371,8 +2391,12 @@ export type StoreCollectionType = | 'Category' /** Product cluster. */ | 'Cluster' + /** Product collection. */ + | 'Collection' /** First level of product categorization. */ | 'Department' + /** Third level of product categorization. */ + | 'SubCategory' /** Currency information. */ export type StoreCurrency = { @@ -2960,6 +2984,14 @@ export type CartItemFragment = { } } +export type SubscribeToNewsletterMutationVariables = Exact<{ + data: IPersonNewsletter +}> + +export type SubscribeToNewsletterMutation = { + subscribeToNewsletter: { id: string } | null +} + export type BrowserProductQueryQueryVariables = Exact<{ locator: Array | IStoreSelectedFacet }> diff --git a/@generated/graphql/persisted.json b/@generated/graphql/persisted.json index 6a972b17..02ec3e93 100644 --- a/@generated/graphql/persisted.json +++ b/@generated/graphql/persisted.json @@ -7,6 +7,7 @@ "HomePageQuery": "query HomePageQuery {\n site {\n siteMetadata {\n title\n description\n titleTemplate\n siteUrl\n }\n }\n}\n", "SearchPageQuery": "query SearchPageQuery {\n site {\n siteMetadata {\n titleTemplate\n title\n description\n }\n }\n}\n", "ValidateCartMutation": "mutation ValidateCartMutation($cart: IStoreCart!) {\n validateCart(cart: $cart) {\n order {\n orderNumber\n acceptedOffer {\n seller {\n identifier\n }\n quantity\n price\n listPrice\n itemOffered {\n sku\n name\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n }\n }\n messages {\n text\n status\n }\n }\n}\n", + "SubscribeToNewsletter": "mutation SubscribeToNewsletter($data: IPersonNewsletter!) {\n subscribeToNewsletter(data: $data) {\n id\n }\n}\n", "BrowserProductQuery": "query BrowserProductQuery($locator: [IStoreSelectedFacet!]!) {\n product(locator: $locator) {\n id: productID\n sku\n name\n gtin\n description\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n brand {\n name\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n seller {\n identifier\n }\n }\n }\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n}\n", "ProductsQuery": "query ProductsQuery($first: Int!, $after: String, $sort: StoreSort!, $term: String!, $selectedFacets: [IStoreSelectedFacet!]!) {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n }\n }\n}\n", "SearchSuggestionsQuery": "query SearchSuggestionsQuery($term: String!, $selectedFacets: [IStoreSelectedFacet!]) {\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n }\n}\n", diff --git a/CHANGELOG.md b/CHANGELOG.md index 5636a0f9..8a9b5e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Newsletter form and `useNewsletter` hook ([#134](https://github.com/vtex-sites/gatsby.store/pull/134)) - Applies new local tokens to `SearchHistory`, `SearchTop`, `SearchDropdown` and `SearchSuggestions` ([#150](https://github.com/vtex-sites/gatsby.store/pull/150)) - Applies CSS Modules to `Incentives` ([#147](https://github.com/vtex-sites/gatsby.store/pull/147)) - Applies new local tokens to `Footer` ([#147](https://github.com/vtex-sites/gatsby.store/pull/147)) diff --git a/package.json b/package.json index 8855fc9c..6c09c7ba 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@envelop/core": "^1.2.0", "@envelop/parser-cache": "^2.2.0", "@envelop/validation-cache": "^2.2.0", - "@faststore/api": "^1.10.8", + "@faststore/api": "^1.10.19", "@faststore/graphql-utils": "^1.10.6", "@faststore/sdk": "^1.10.6", "@faststore/ui": "^1.10.7", diff --git a/src/components/sections/Newsletter/Newsletter.tsx b/src/components/sections/Newsletter/Newsletter.tsx index f5c4950b..1f8094a8 100644 --- a/src/components/sections/Newsletter/Newsletter.tsx +++ b/src/components/sections/Newsletter/Newsletter.tsx @@ -1,6 +1,9 @@ import type { ComponentPropsWithRef, FormEvent, ReactNode } from 'react' import { forwardRef, useRef } from 'react' -import { Form, Label, Input, Button } from '@faststore/ui' +import { Form, Label, Input, LoadingButton } from '@faststore/ui' +import { useNewsletter } from 'src/sdk/newsletter/useNewsletter' + +import Section from '../Section' export interface NewsletterProps extends Omit, 'title' | 'onSubmit'> { @@ -12,49 +15,63 @@ export interface NewsletterProps * A subtitle for the section. */ subtitle?: ReactNode - /** - * Callback function when submitted. - */ - onSubmit: (value: string) => void } const Newsletter = forwardRef( - function Newsletter({ title, subtitle, onSubmit, ...otherProps }, ref) { + function Newsletter({ title, subtitle, ...otherProps }, ref) { + const { subscribeUser, loading } = useNewsletter() + const nameInputRef = useRef(null) const emailInputRef = useRef(null) const handleSubmit = (event: FormEvent) => { event.preventDefault() + subscribeUser({ + data: { + name: nameInputRef.current?.value ?? '', + email: emailInputRef.current?.value ?? '', + }, + }) + const formElement = event.currentTarget as HTMLFormElement - if (emailInputRef.current?.value !== '') { - onSubmit(emailInputRef.current?.value ?? '') - } + formElement.reset() } return ( -
+
-
+
{title} {Boolean(subtitle) && subtitle}
-
+
+ + - + + Subscribe +
-
+
) } ) diff --git a/src/sdk/newsletter/useNewsletter.ts b/src/sdk/newsletter/useNewsletter.ts new file mode 100644 index 00000000..5d9c4961 --- /dev/null +++ b/src/sdk/newsletter/useNewsletter.ts @@ -0,0 +1,31 @@ +import { gql } from '@faststore/graphql-utils' + +import type { + SubscribeToNewsletterMutation as Mutation, + SubscribeToNewsletterMutationVariables as Variables, +} from '../../../@generated/graphql/index' +import { useLazyQuery } from '../graphql/useLazyQuery' + +export const mutation = gql` + mutation SubscribeToNewsletter($data: IPersonNewsletter!) { + subscribeToNewsletter(data: $data) { + id + } + } +` + +export const useNewsletter = () => { + const [subscribeUser, { data, error, isValidating: loading }] = useLazyQuery< + Mutation, + Variables + >(mutation, { + data: { name: '', email: '' }, + }) + + return { + subscribeUser, + data, + error, + loading, + } +} diff --git a/yarn.lock b/yarn.lock index cefd9ef4..1c46eda1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1927,10 +1927,10 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@faststore/api@^1.10.8": - version "1.10.8" - resolved "https://registry.yarnpkg.com/@faststore/api/-/api-1.10.8.tgz#be0d7297e4514e6d328b42cdf722cb6099b2c08b" - integrity sha512-os6ebn8j3N1lVekZLCenDwNJI+CR8JQ04Fz4CimYrQhD461q8Hl7BzfVe5FB+aERzHLBlrUm1OkE5IaCkKD7VA== +"@faststore/api@^1.10.19": + version "1.10.19" + resolved "https://registry.yarnpkg.com/@faststore/api/-/api-1.10.19.tgz#daa99085130e65d5d62b442f8138b29e38914dcb" + integrity sha512-DK6lEU/4MMoEbLgCOU+BB8s82tkpNKp12YaWdwdxmr61hUVOkwUY3Qs3wX27nn2rL2xPgvNu0/MBIdP7GxPziQ== dependencies: "@graphql-tools/schema" "^8.2.0" "@rollup/plugin-graphql" "^1.0.0" @@ -21018,8 +21018,10 @@ watchpack@^1.7.4: resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== dependencies: + chokidar "^3.4.1" graceful-fs "^4.1.2" neo-async "^2.5.0" + watchpack-chokidar2 "^2.0.1" optionalDependencies: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1"