diff --git a/.env b/.env new file mode 100644 index 00000000..55a88b4b --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +REACT_APP_HUBSPOT_PORTAL_ID=25945010 +REACT_APP_HUBSPOT_FORM_GUID=991e2a09-77c2-4428-9242-ebf26bfc6c64 diff --git a/jsconfig.json b/jsconfig.json index 5875dc5b..ef66e1dc 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { - "baseUrl": "src" + "baseUrl": "src", + "jsx": "react" }, "include": ["src"] } diff --git a/src/components/NewsletterForm.jsx b/src/components/NewsletterForm.jsx new file mode 100644 index 00000000..be78b344 --- /dev/null +++ b/src/components/NewsletterForm.jsx @@ -0,0 +1,74 @@ +import React from 'react' +import styled from 'styled-components' + +import useNewsletter from 'hooks/useNewsletter' + +const Input = styled.input` + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid ${(p) => p.theme.colors.gray[8]}; + border-radius: 8px; + font-size: 0.875rem; + color: ${(p) => p.theme.colors.gray[0]}; + background: ${(p) => p.theme.colors.white}; + margin: 1rem 0; + + &:focus { + outline: none; + border-color: ${(p) => p.theme.colors.main.default}; + } + + &::placeholder { + color: ${(p) => p.theme.colors.gray[6]}; + } +` + +const Button = styled.button` + width: 100%; + padding: 0.75rem 1rem; + background: ${(p) => p.theme.colors.main.default}; + color: white; + border: none; + border-radius: 8px; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background: ${(p) => p.theme.colors.main.dark}; + } +` + +const NewsletterForm = () => { + const [email, setEmail] = React.useState('') + const { subscribe } = useNewsletter() + const [status, setStatus] = React.useState('idle') + + const handleSubmit = (e) => { + e.preventDefault() + setStatus('loading') + subscribe(email) + .then(() => setStatus('success')) + .catch(() => setStatus('error')) + } + + return ( +
+ setEmail(e.target.value)} + disabled={status === 'loading'} + required + /> + + {status === 'loading' &&

Loading...

} + {status === 'success' &&

Success!

} + {status === 'error' &&

Error

} +
+ ) +} + +export default NewsletterForm diff --git a/src/components/RightPanel.jsx b/src/components/RightPanel.jsx new file mode 100644 index 00000000..a43c168a --- /dev/null +++ b/src/components/RightPanel.jsx @@ -0,0 +1,193 @@ +import React from 'react' +import styled from 'styled-components' +import Link from 'components/Link' +import Typography from 'components/Typography' +import AcademicHatIcon from 'components/icons/heroicons/AcademicHatIcon' +import LifebuoyIcon from 'components/icons/heroicons/LifebuoyIcon' +import ChatBubbleIcon from 'components/icons/heroicons/ChatBubbleIcon' +import MenuBarsIcon from 'components/icons/heroicons/MenuBarsIcon' +import NewsletterForm from 'components/NewsletterForm' + +const PanelWrapper = styled.div` + position: fixed; + top: 0; + right: 0; + width: 430px; + height: 100vh; + background: ${(p) => p.theme.colors.white}; + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); + transform: translateX(${({ isOpen }) => (isOpen ? '0' : '100%')}); + transition: transform 0.3s ease-in-out; + padding: 2rem 2.5rem; + z-index: 10; +` + +const Title = styled.h2` + color: ${(p) => p.theme.colors.main.default}; + font-size: 1.5rem; + font-weight: 600; + margin: 0; +` + +const SectionTitle = styled.h3` + color: ${(p) => p.theme.colors.gray[0]}; + font-weight: 600; + margin-bottom: 2rem; +` + +const Header = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + height: 3rem; + margin-bottom: 2rem; +` + +const CloseButton = styled.button` + background: none; + border: none; + cursor: pointer; + font-size: 2rem; + color: ${(p) => p.theme.colors.gray[0]}; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 24px; + height: 24px; + color: ${(p) => p.theme.colors.gray[0]}; + } +` + +const HelpLink = styled(Link)` + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; + margin-bottom: 0.75rem; + background-color: transparent; + border: 1px solid ${(p) => p.theme.colors.gray[10]}; + border-radius: 8px; + box-shadow: 0px 4px 6px ${(p) => p.theme.colors.gray[10]}10; + text-decoration: none; + color: ${(p) => p.theme.colors.gray[0]}; + transition: all 0.2s; + + svg { + width: 20px; + height: 20px; + color: ${(p) => p.theme.colors.gray[0]}; + transition: color 0.1s; + } + + &:hover { + box-shadow: none; + border-color: ${(p) => p.theme.colors.main.default}; + text-decoration: none; + color: ${(p) => p.theme.colors.gray[0]}; + + svg { + // color: ${(p) => p.theme.colors.main.default}; + } + } +` + +const Section = styled.div` + margin-bottom: 2.5rem; +` +const CloudButton = styled.button` + width: 100%; + padding: 0.75rem 1rem; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 0.75rem; + background: linear-gradient(269.85deg, #ff1786 0%, #8e33de 100%); + color: white; + border: none; + border-radius: 8px; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: transform 0.2s; + + &:hover { + transform: translateY(-2px); + } +` + +const CloudCardLink = styled(Link)` + text-decoration: none; + display: block; + + &:hover { + text-decoration: none; + } +` + +const RightPanel = ({ isOpen, onClose }) => ( + +
+ Getting started + + + +
+ +
+ Try Meilisearch Cloud + + Streamline your experience with search analytics, monitoring, and more. + + + Start free trial + +
+ +
+ Need help? + + Check out our resources to get started. + + + + + Documentation + + + + Help center + + + + Community + +
+ +
+ Stay up to date + + Get monthly updates about new features and tips to get the the most out + of Meilisearch. + + +
+
+) + +export default RightPanel diff --git a/src/components/RightPanel/index.js b/src/components/RightPanel/index.js deleted file mode 100644 index b7444f71..00000000 --- a/src/components/RightPanel/index.js +++ /dev/null @@ -1,248 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import Link from 'components/Link' -import Typography from 'components/Typography' -import AcademicHatIcon from 'components/icons/heroicons/AcademicHatIcon' -import LifebuoyIcon from 'components/icons/heroicons/LifebuoyIcon' -import ChatBubbleIcon from 'components/icons/heroicons/ChatBubbleIcon' -import MenuBarsIcon from 'components/icons/heroicons/MenuBarsIcon' - -const PanelWrapper = styled.div` - position: fixed; - top: 0; - right: 0; - width: 430px; - height: 100vh; - background: ${(p) => p.theme.colors.white}; - box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); - transform: translateX(${({ isOpen }) => (isOpen ? '0' : '100%')}); - transition: transform 0.3s ease-in-out; - padding: 2rem 2.5rem; - z-index: 10; -` - -const Title = styled.h2` - color: ${(p) => p.theme.colors.main.default}; - font-size: 1.5rem; - font-weight: 600; - margin: 0; -` - -const SectionTitle = styled.h3` - color: ${(p) => p.theme.colors.gray[0]}; - font-weight: 600; - margin-bottom: 2rem; -` - -const Header = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - height: 3rem; - margin-bottom: 2rem; -` - -const CloseButton = styled.button` - background: none; - border: none; - cursor: pointer; - font-size: 2rem; - color: ${(p) => p.theme.colors.gray[0]}; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - - svg { - width: 24px; - height: 24px; - color: ${(p) => p.theme.colors.gray[0]}; - } -` - -const HelpLink = styled(Link)` - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.75rem 1rem; - margin-bottom: 0.75rem; - background-color: transparent; - border: 1px solid ${(p) => p.theme.colors.gray[10]}; - border-radius: 8px; - box-shadow: 0px 4px 6px ${(p) => p.theme.colors.gray[10]}10; - text-decoration: none; - color: ${(p) => p.theme.colors.gray[0]}; - transition: all 0.2s; - - svg { - width: 20px; - height: 20px; - color: ${(p) => p.theme.colors.gray[0]}; - transition: color 0.1s; - } - - &:hover { - box-shadow: none; - border-color: ${(p) => p.theme.colors.main.default}; - text-decoration: none; - color: ${(p) => p.theme.colors.gray[0]}; - - svg { - // color: ${(p) => p.theme.colors.main.default}; - } - } -` - -const Input = styled.input` - width: 100%; - padding: 0.75rem 1rem; - border: 1px solid ${(p) => p.theme.colors.gray[8]}; - border-radius: 8px; - font-size: 0.875rem; - color: ${(p) => p.theme.colors.gray[0]}; - background: ${(p) => p.theme.colors.white}; - margin: 1rem 0; - - &:focus { - outline: none; - border-color: ${(p) => p.theme.colors.main.default}; - } - - &::placeholder { - color: ${(p) => p.theme.colors.gray[6]}; - } -` - -const Button = styled.button` - width: 100%; - padding: 0.75rem 1rem; - background: ${(p) => p.theme.colors.main.default}; - color: white; - border: none; - border-radius: 8px; - font-size: 0.875rem; - font-weight: 600; - cursor: pointer; - transition: background-color 0.2s; - - &:hover { - background: ${(p) => p.theme.colors.main.dark}; - } -` - -const Section = styled.div` - margin-bottom: 2.5rem; -` -const CloudButton = styled.button` - width: 100%; - padding: 0.75rem 1rem; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - gap: 0.75rem; - background: linear-gradient(269.85deg, #ff1786 0%, #8e33de 100%); - color: white; - border: none; - border-radius: 8px; - font-size: 0.875rem; - font-weight: 600; - cursor: pointer; - transition: transform 0.2s; - - &:hover { - transform: translateY(-2px); - } -` - -const CloudCardLink = styled(Link)` - text-decoration: none; - display: block; - - &:hover { - text-decoration: none; - } -` - -const RightPanel = ({ isOpen, onClose }) => { - const [email, setEmail] = React.useState('') - - const handleSubscribe = (e) => { - e.preventDefault() - // TODO: Implement newsletter subscription - console.log('Subscribe with email:', email) - } - - return ( - -
- Getting started - - - -
- -
- Try Meilisearch Cloud - - Streamline your experience with search analytics, monitoring, and - more. - - - Start free trial - -
- -
- Need help? - - Check out our resources to get started. - - - - - Documentation - - - - Help center - - - - Community - -
- -
- Stay up to date - - Get monthly updates about new features and tips to get the the most - out of Meilisearch. - -
- setEmail(e.target.value)} - required - /> - -
-
-
- ) -} -export default RightPanel diff --git a/src/hooks/useNewsletter.js b/src/hooks/useNewsletter.js new file mode 100644 index 00000000..a420b025 --- /dev/null +++ b/src/hooks/useNewsletter.js @@ -0,0 +1,57 @@ +import version from '../version/version' + +const PORTAL_ID = process.env.REACT_APP_HUBSPOT_PORTAL_ID +const FORM_GUID = process.env.REACT_APP_HUBSPOT_FORM_GUID + +const PAGE_NAME = + process.env.NODE_ENV === 'development' + ? `Mini-dashboard (dev)` + : `Mini-dashboard v${version}` + +function getBody({ email, pageName }) { + return { + fields: [ + { + objectTypeId: '0-1', + name: 'email', + value: email, + }, + ], + context: { + pageName, + }, + legalConsentOptions: { + consent: { + consentToProcess: true, + text: 'I agree to allow Meilisearch to store and process my personal data.', + communications: [ + { + value: true, + subscriptionTypeId: 999, + text: 'I agree to receive marketing communications from Meilisearch.', + }, + ], + }, + }, + } +} + +export default function useNewsletter() { + const endpoint = `https://api.hsforms.com/submissions/v3/integration/submit/${PORTAL_ID}/${FORM_GUID}` + + const subscribe = (email) => + fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( + getBody({ + email, + pageName: PAGE_NAME, + }) + ), + }) + + return { subscribe } +}