Compare commits

...

2 Commits

Author SHA1 Message Date
EleanorMao
06358334c4 feat: login & home 2023-04-24 23:24:17 +08:00
EleanorMao
c1b7cdcbfa feat: add icons 2023-04-24 09:28:33 +08:00
33 changed files with 644 additions and 23 deletions

View File

@ -1,13 +1,19 @@
import React from 'react';
import { Routes, Route } from "react-router-dom";
import { BreakpointProvider } from './components/Breakpoint'
import { Header } from './components/Layout/Header'
import { Footer } from './components/Layout/Footer'
import { Layout } from './components/Layout'
import { Home } from './pages/activation/home'
import { Login } from './pages/activation/login'
function App() {
return (
<BreakpointProvider>
<Header showActiveKit showMenu />
<Footer />
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="/login" element={<Login />} />
</Route>
</Routes>
</BreakpointProvider>
);
}

View File

@ -66,7 +66,7 @@ export const useBreakpoint = (props: BreakpointProps): boolean => {
return size.width === breakpointList[key as keyof typeof breakpointList]
}
if (key === 'width') {
return matchWidth(3000, props.width?.min, props.width?.max)
return matchWidth(size.width, props.width?.min, props.width?.max)
}
if (key === 'landscape' || key === 'portrait') {
return key === orientation

View File

@ -0,0 +1,20 @@
import React, { FC, PropsWithChildren } from 'react'
import { InfoCircleOutlined } from '@ant-design/icons'
import styled from 'styled-components'
const StyledIcon = styled(InfoCircleOutlined)`
display: inline-block;
margin-right: 6px;
font-size: 14px;
line-height: 16px;
vertical-align: top;
${props => props.theme.breakpoints.down('s')} {
font-size: 8px;
margin-right: 4px;
line-height: 10px;
}
`
export const ErrorMessage: FC<PropsWithChildren<{ message?: string }>> = ({ message, children }) => {
return <><StyledIcon />{children || message}</>
}

View File

@ -0,0 +1,31 @@
import React, { FC, PropsWithChildren } from 'react'
import { InfoCircleOutlined } from '@ant-design/icons'
import { Tooltip } from '../Tooltip'
import styled from 'styled-components'
const StyledLabel = styled.span`
position: relative;
display: inline-block;
width: 100%;
`
const StyledIcon = styled(InfoCircleOutlined)`
position: absolute;
font-size: 16px;
color: #000022;
right: 0;
`
export const LabelWithTooltip: FC<PropsWithChildren<{ title: string; primary?: boolean }>> = ({
primary,
children,
title
}) => {
return (
<StyledLabel>
{children}
<Tooltip closeable title={title} primary={primary} placement="left">
<StyledIcon />
</Tooltip>
</StyledLabel>
)
}

View File

@ -5,3 +5,5 @@ export * from './Select'
export * from './Chekbox'
export * from './Radio'
export * from './RadioGroup'
export * from './ErrorMessage'
export * from './LabelWithTooltip'

View File

@ -119,12 +119,12 @@ const TinyFooter = styled.footer`
const StyledFooter: FC<PropsWithChildren> = ({ children }) => {
return (
<>
<Breakpoint s down>
<Breakpoint width={{ max: 520 }}>
<TinyFooter>
{children}
</TinyFooter>
</Breakpoint>
<Breakpoint s up>
<Breakpoint width={{ min: 520 }}>
<LargeFooter>
{children}
</LargeFooter>
@ -133,9 +133,9 @@ const StyledFooter: FC<PropsWithChildren> = ({ children }) => {
)
}
export const Footer: FC = () => {
export const Footer: FC<{ className?: string }> = (props) => {
return (
<StyledFooter>
<StyledFooter {...props}>
<Links>
{
FooterLinks.map((link, index) => (

View File

@ -20,7 +20,6 @@ const StyledHeader = styled.header`
border-bottom: 2px solid #000022;
}
`
const StyledLogo = styled(Logo)`
width: 171px;
height: 41px;
@ -78,11 +77,12 @@ const StyledButton = styled(Button)`
height: 58px;
${props => props.theme.breakpoints.down(600)} {
width: 160px;
width: 130px;
}
`
export interface HeaderProps {
className?: string
showMenu?: boolean
onClickMenu?: () => void
showActiveKit?: boolean;
@ -101,10 +101,11 @@ export const Header: FC<HeaderProps> = ({
showMenu,
onClickMenu,
showActiveKit,
activeKitHref
activeKitHref,
className
}) => {
return (
<StyledHeader>
<StyledHeader className={className}>
{showMenu &&
<Breakpoint s down>
<StyledMenuIcon component={Menu} onClick={onClickMenu} />
@ -115,7 +116,8 @@ export const Header: FC<HeaderProps> = ({
<Breakpoint s up>
<StyledButton href={activeKitHref}>Activate Kit</StyledButton>
</Breakpoint>}
<StyledCartWrapper target="_blank" rel="noopener noreferrer" href="https://ihealthlabs.com/products/checkmesafe-home-collection-kit-C2">
<StyledCartWrapper target="_blank" rel="noopener noreferrer"
href="https://ihealthlabs.com/products/checkmesafe-home-collection-kit-C2">
<Icon component={Cart} />
<StyledText>SHOP</StyledText>
</StyledCartWrapper>

View File

@ -0,0 +1,36 @@
import React, { FC, PropsWithChildren } from 'react'
import styled from 'styled-components'
import { Header, HeaderProps } from './Header'
import { Footer } from './Footer'
import { Outlet } from 'react-router-dom'
const StyledSection = styled.section`
display: flex;
min-height: 100vh;
flex-direction: column;
`
const StyledHeader = styled(Header)`
flex: 0 0 auto;
`
const StyledFooter = styled(Footer)`
flex: 0 0 auto;
`
const StyledMain = styled.main`
flex: auto;
`
export const Layout: FC<PropsWithChildren<Omit<HeaderProps, 'className'>>> = ({ children, ...headerProps }) => {
return (
<StyledSection>
<StyledHeader {...headerProps} />
<StyledMain>
<Outlet />
{children}
</StyledMain>
<StyledFooter />
</StyledSection>
)
}

View File

@ -0,0 +1 @@
export * from './Layout'

View File

@ -1,15 +1,19 @@
import React, { FC, useCallback } from 'react'
import React, { FC, ReactNode, useCallback } from 'react'
import { Tooltip as AntdTooltip, TooltipProps as AntdTooltipProps } from 'antd'
import { ReactComponent as CloseIcon } from '../../icons/close.svg'
import styled from 'styled-components'
export type TooltipProps = AntdTooltipProps & { closeable?: boolean; primary?: boolean }
export type TooltipProps = Omit<AntdTooltipProps, 'title'> & {
closeable?: boolean; primary?: boolean;
title: ReactNode
}
const Close = styled.span`
line-height: 17px;
line-height: 25px;
vertical-align: middle;
cursor: pointer;
padding-left: 4px;
display: inline-block;
> svg {
width: 14px;
@ -27,7 +31,8 @@ export const Tooltip: FC<TooltipProps> = ({ closeable, primary, title, overlayCl
trigger="click"
overlayClassName={`health-tooltip${primary ? '-highlighted' : ''} ${overlayClassName || ''}`}
{...props}
title={<>{title} {closeable ? <Close><CloseIcon onClick={handleClick} /></Close> : null}</>}
title={<><span style={{ display: 'inline-block' }}>{title}</span> {closeable ?
<Close><CloseIcon onClick={handleClick} /></Close> : null}</>}
/>
)
}

4
src/icons/bell.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="24" height="26" viewBox="0 0 24 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9999 2.02502C9.90788 2.02502 7.83999 2.88157 6.36072 4.36085C4.88144 5.84012 4.02489 7.90801 4.02489 10C4.02489 14.6015 3.06305 17.3784 2.13697 18.9852C2.13807 18.9883 2.13958 18.9916 2.14154 18.9951C2.14258 18.9969 2.14464 19 2.14464 19H21.8551C21.8551 19 21.8572 18.9969 21.8582 18.9951C21.8602 18.9916 21.8617 18.9883 21.8628 18.9852C20.9367 17.3784 19.9749 14.6015 19.9749 10C19.9749 7.90801 19.1183 5.84012 17.6391 4.36085C16.1598 2.88157 14.0919 2.02502 11.9999 2.02502ZM4.9465 2.94663C6.80085 1.09229 9.37745 0.0250244 11.9999 0.0250244C14.6223 0.0250244 17.1989 1.09229 19.0533 2.94663C20.9076 4.80098 21.9749 7.37758 21.9749 10C21.9749 14.3334 22.8817 16.7534 23.6028 17.999L23.6034 18C24.3493 19.2919 23.4168 20.9946 21.8784 21L21.8749 21H2.12489L2.12137 21C0.582952 20.9946 -0.349523 19.2919 0.39637 18L0.396965 17.999C1.11809 16.7534 2.02489 14.3334 2.02489 10C2.02489 7.37758 3.09215 4.80098 4.9465 2.94663Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.9999 19C8.55219 19 8.9999 19.4477 8.9999 20V21C8.9999 21.7956 9.31597 22.5587 9.87858 23.1213C10.4412 23.6839 11.2043 24 11.9999 24C12.7956 24 13.5586 23.6839 14.1212 23.1213C14.6838 22.5587 14.9999 21.7956 14.9999 21V20C14.9999 19.4477 15.4476 19 15.9999 19C16.5522 19 16.9999 19.4477 16.9999 20V21C16.9999 22.3261 16.4731 23.5979 15.5354 24.5355C14.5978 25.4732 13.326 26 11.9999 26C10.6738 26 9.40205 25.4732 8.46437 24.5355C7.52669 23.5979 6.9999 22.3261 6.9999 21V20C6.9999 19.4477 7.44762 19 7.9999 19Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

3
src/icons/caretLeft.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.7071 5.29289C21.0976 5.68342 21.0976 6.31658 20.7071 6.70711L11.4142 16L20.7071 25.2929C21.0976 25.6834 21.0976 26.3166 20.7071 26.7071C20.3166 27.0976 19.6834 27.0976 19.2929 26.7071L9.29289 16.7071C8.90237 16.3166 8.90237 15.6834 9.29289 15.2929L19.2929 5.29289C19.6834 4.90237 20.3166 4.90237 20.7071 5.29289Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 487 B

4
src/icons/check.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2236 9.30973C19.6048 9.70934 19.5899 10.3423 19.1903 10.7236L11.8528 17.7236C11.4662 18.0924 10.8579 18.0921 10.4716 17.723L6.80911 14.223C6.40983 13.8414 6.39547 13.2084 6.77704 12.8091C7.1586 12.4098 7.7916 12.3955 8.19089 12.777L11.1631 15.6174L17.8097 9.27645C18.2093 8.89522 18.8423 8.91012 19.2236 9.30973Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 2C6.92487 2 2 6.92487 2 13C2 19.0751 6.92487 24 13 24C19.0751 24 24 19.0751 24 13C24 6.92487 19.0751 2 13 2ZM0 13C0 5.8203 5.8203 0 13 0C20.1797 0 26 5.8203 26 13C26 20.1797 20.1797 26 13 26C5.8203 26 0 20.1797 0 13Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 775 B

5
src/icons/download.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0429 13.0429C10.4334 12.6524 11.0666 12.6524 11.4571 13.0429L16 17.5858L20.5429 13.0429C20.9334 12.6524 21.5666 12.6524 21.9571 13.0429C22.3476 13.4334 22.3476 14.0666 21.9571 14.4571L16.7071 19.7071C16.3166 20.0976 15.6834 20.0976 15.2929 19.7071L10.0429 14.4571C9.65237 14.0666 9.65237 13.4334 10.0429 13.0429Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 4C16.5523 4 17 4.44772 17 5V19C17 19.5523 16.5523 20 16 20C15.4477 20 15 19.5523 15 19V5C15 4.44772 15.4477 4 16 4Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 18C5.55228 18 6 18.4477 6 19V26H26V19C26 18.4477 26.4477 18 27 18C27.5523 18 28 18.4477 28 19V26C28 26.5304 27.7893 27.0391 27.4142 27.4142C27.0391 27.7893 26.5304 28 26 28H6C5.46957 28 4.96086 27.7893 4.58579 27.4142C4.21071 27.0391 4 26.5304 4 26V19C4 18.4477 4.44772 18 5 18Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 1023 B

3
src/icons/gear.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7178 2.66085C11.9925 2.57219 12.2921 2.60651 12.5395 2.755L15.4626 4.50886C15.8209 4.49285 16.1798 4.49286 16.5381 4.50889L19.449 2.7669C19.6945 2.62003 19.9911 2.58528 20.2638 2.67145C22.1741 3.27497 23.9304 4.28646 25.4111 5.63587C25.6234 5.82931 25.7422 6.10468 25.7374 6.39182L25.6802 9.78962C25.8776 10.0927 26.0587 10.4062 26.2228 10.7285L29.186 12.376C29.4355 12.5147 29.6138 12.7535 29.676 13.0321C30.112 14.9847 30.1166 17.0089 29.6895 18.9635C29.6281 19.2445 29.4487 19.4854 29.197 19.6248L26.2224 21.2723C26.0584 21.5944 25.8774 21.9075 25.6802 22.2104L25.7374 25.6082C25.7422 25.8953 25.6234 26.1707 25.4111 26.3641C23.9335 27.7106 22.1846 28.725 20.2822 29.3391C20.0076 29.4278 19.708 29.3935 19.4605 29.245L16.5374 27.4911C16.1791 27.5071 15.8203 27.5071 15.462 27.4911L12.551 29.2331C12.3056 29.38 12.009 29.4147 11.7363 29.3285C9.82599 28.725 8.06968 27.7135 6.58896 26.3641C6.37668 26.1707 6.25783 25.8953 6.26268 25.6081L6.31994 22.218C6.12371 21.9117 5.94278 21.5959 5.7778 21.2718L2.81411 19.624C2.5646 19.4853 2.38627 19.2465 2.32407 18.9679C1.88811 17.0153 1.88351 14.9911 2.31059 13.0365C2.37199 12.7555 2.55142 12.5146 2.80304 12.3752L5.77768 10.7277C5.94167 10.4056 6.12265 10.0925 6.31988 9.78961L6.26268 6.39182C6.25784 6.10466 6.37671 5.82929 6.58899 5.63585C8.0666 4.28938 9.81543 3.27496 11.7178 2.66085ZM8.27018 6.82083L8.32489 10.0707C8.32844 10.2812 8.26543 10.4875 8.14485 10.6601C7.87663 11.0441 7.6418 11.4504 7.44295 11.8745C7.35355 12.0652 7.20625 12.2228 7.02204 12.3248L4.17771 13.9001C3.92892 15.2891 3.93237 16.7117 4.18791 18.0995L7.02347 19.676C7.20629 19.7776 7.35263 19.9341 7.4419 20.1232C7.64511 20.5539 7.88214 20.9677 8.15075 21.3609C8.26754 21.5319 8.32839 21.7349 8.32489 21.9419L8.27021 25.179C9.35109 26.0921 10.5871 26.8038 11.9194 27.2803L14.699 25.6169C14.8751 25.5116 15.0793 25.4629 15.2839 25.4775C15.7607 25.5116 16.2394 25.5116 16.7162 25.4775C16.9212 25.4629 17.1258 25.5117 17.302 25.6175L20.0906 27.2906C21.4185 26.8066 22.6506 26.0918 23.7299 25.1792L23.6752 21.9293C23.6716 21.7188 23.7346 21.5125 23.8552 21.3399C24.1234 20.9559 24.3583 20.5496 24.5571 20.1255C24.6465 19.9348 24.7938 19.7772 24.978 19.6752L27.8224 18.0999C28.0712 16.7109 28.0677 15.2883 27.8122 13.9005L24.9766 12.324C24.793 12.2219 24.6463 12.0647 24.5571 11.8745C24.3583 11.4504 24.1234 11.0441 23.8552 10.6601C23.7346 10.4875 23.6716 10.2812 23.6752 10.0707L23.7299 6.82096C22.649 5.90786 21.413 5.19614 20.0806 4.7197L17.301 6.38308C17.125 6.48843 16.9208 6.53708 16.7162 6.52244C16.2394 6.48833 15.7607 6.48833 15.2839 6.52244C15.0789 6.53711 14.8743 6.48824 14.698 6.38248L11.9095 4.70935C10.5815 5.19334 9.34945 5.90817 8.27018 6.82083ZM16 11C13.2386 11 11 13.2386 11 16C11 18.7614 13.2386 21 16 21C18.7615 21 21 18.7614 21 16C21 13.2386 18.7615 11 16 11ZM9.00003 16C9.00003 12.134 12.134 8.99999 16 8.99999C19.866 8.99999 23 12.134 23 16C23 19.866 19.866 23 16 23C12.134 23 9.00003 19.866 9.00003 16Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

3
src/icons/heart.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="13" height="10" viewBox="0 0 13 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.3242 0.894449C10.7723 0.343906 10.0248 0.0343085 9.24526 0.0333569C8.4657 0.0324053 7.71747 0.340177 7.16421 0.889371L6.49999 1.50636L5.83526 0.887339C5.28213 0.335757 4.53254 0.0264956 3.75139 0.0275908C2.97024 0.028686 2.22152 0.340048 1.66993 0.893179C1.11835 1.44631 0.809088 2.1959 0.810184 2.97705C0.811279 3.75821 1.12264 4.50693 1.67577 5.05851L6.21308 9.66234C6.25088 9.70072 6.29594 9.73121 6.34563 9.75201C6.39532 9.77282 6.44866 9.78354 6.50253 9.78354C6.5564 9.78354 6.60974 9.77282 6.65943 9.75201C6.70912 9.73121 6.75418 9.70072 6.79198 9.66234L11.3242 5.05851C11.8762 4.50621 12.1863 3.75732 12.1863 2.97648C12.1863 2.19564 11.8762 1.44675 11.3242 0.894449ZM10.7478 4.48773L6.49999 8.79601L2.2496 4.48367C1.8496 4.08367 1.62488 3.54115 1.62488 2.97546C1.62488 2.40978 1.8496 1.86726 2.2496 1.46726C2.6496 1.06726 3.19212 0.842543 3.7578 0.842543C4.32349 0.842543 4.86601 1.06726 5.26601 1.46726L5.27616 1.47742L6.22323 2.35847C6.29841 2.42843 6.3973 2.46733 6.49999 2.46733C6.60269 2.46733 6.70157 2.42843 6.77675 2.35847L7.72382 1.47742L7.73397 1.46726C8.13424 1.06753 8.67691 0.843178 9.2426 0.843559C9.80828 0.84394 10.3506 1.06902 10.7504 1.46929C11.1501 1.86956 11.3745 2.41223 11.3741 2.97792C11.3737 3.5436 11.1486 4.08597 10.7484 4.4857L10.7478 4.48773Z" fill="#FF5200"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

3
src/icons/history.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.125 4.25002V7.64612L11.9469 9.33909C12.089 9.42445 12.1914 9.56279 12.2316 9.72366C12.2717 9.88453 12.2463 10.0548 12.1609 10.1969C12.0756 10.339 11.9372 10.4414 11.7764 10.4816C11.6155 10.5217 11.4453 10.4963 11.3031 10.411L8.17812 8.53596C8.08563 8.48039 8.00911 8.40183 7.95599 8.30791C7.90287 8.21399 7.87497 8.10792 7.875 8.00002V4.25002C7.875 4.08426 7.94085 3.92529 8.05806 3.80808C8.17527 3.69087 8.33424 3.62502 8.5 3.62502C8.66576 3.62502 8.82473 3.69087 8.94194 3.80808C9.05915 3.92529 9.125 4.08426 9.125 4.25002ZM8.5 0.500023C7.51406 0.497567 6.53742 0.690675 5.62661 1.06817C4.71579 1.44566 3.88889 2.00005 3.19375 2.69924C2.62578 3.27424 2.12109 3.82737 1.625 4.40627V3.00002C1.625 2.83426 1.55915 2.67529 1.44194 2.55808C1.32473 2.44087 1.16576 2.37502 1 2.37502C0.83424 2.37502 0.675268 2.44087 0.558058 2.55808C0.440848 2.67529 0.375 2.83426 0.375 3.00002V6.12502C0.375 6.29078 0.440848 6.44975 0.558058 6.56697C0.675268 6.68418 0.83424 6.75002 1 6.75002H4.125C4.29076 6.75002 4.44973 6.68418 4.56694 6.56697C4.68415 6.44975 4.75 6.29078 4.75 6.12502C4.75 5.95926 4.68415 5.80029 4.56694 5.68308C4.44973 5.56587 4.29076 5.50002 4.125 5.50002H2.32812C2.88672 4.84221 3.44297 4.22268 4.07734 3.58049C4.94598 2.71186 6.05133 2.11847 7.25529 1.87446C8.45924 1.63045 9.70841 1.74665 10.8467 2.20853C11.985 2.67041 12.9619 3.45749 13.6555 4.47144C14.349 5.48538 14.7283 6.68121 14.7461 7.90952C14.7639 9.13782 14.4193 10.3441 13.7554 11.3777C13.0915 12.4113 12.1377 13.2263 11.0132 13.7209C9.88879 14.2155 8.6435 14.3678 7.43299 14.1587C6.22249 13.9496 5.10043 13.3885 4.20703 12.5453C4.14732 12.4889 4.07708 12.4448 4.00032 12.4155C3.92356 12.3862 3.84179 12.3724 3.75967 12.3747C3.67754 12.377 3.59668 12.3955 3.5217 12.429C3.44672 12.4626 3.37908 12.5106 3.32266 12.5703C3.26623 12.63 3.22212 12.7003 3.19283 12.777C3.16355 12.8538 3.14967 12.9356 3.152 13.0177C3.15432 13.0998 3.17279 13.1807 3.20636 13.2557C3.23993 13.3306 3.28795 13.3983 3.34766 13.4547C4.23785 14.2948 5.32012 14.9042 6.5 15.2298C7.67989 15.5554 8.92153 15.5873 10.1166 15.3226C11.3116 15.058 12.4237 14.505 13.3559 13.7117C14.288 12.9184 15.0118 11.9091 15.4642 10.7718C15.9165 9.63442 16.0836 8.40366 15.9509 7.18689C15.8182 5.97011 15.3897 4.80431 14.7029 3.79122C14.016 2.77813 13.0916 1.94854 12.0104 1.37485C10.9292 0.801167 9.72399 0.50082 8.5 0.500023Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

5
src/icons/info.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 2C6.92487 2 2 6.92487 2 13C2 19.0751 6.92487 24 13 24C19.0751 24 24 19.0751 24 13C24 6.92487 19.0751 2 13 2ZM0 13C0 5.8203 5.8203 0 13 0C20.1797 0 26 5.8203 26 13C26 20.1797 20.1797 26 13 26C5.8203 26 0 20.1797 0 13Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 6C13.5523 6 14 6.44772 14 7V14C14 14.5523 13.5523 15 13 15C12.4477 15 12 14.5523 12 14V7C12 6.44772 12.4477 6 13 6Z" fill="#000022"/>
<path d="M13 20C13.8284 20 14.5 19.3284 14.5 18.5C14.5 17.6716 13.8284 17 13 17C12.1716 17 11.5 17.6716 11.5 18.5C11.5 19.3284 12.1716 20 13 20Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 741 B

4
src/icons/link.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4525 3.17426L11.9947 5.63207C11.6041 6.02259 10.971 6.02259 10.5804 5.63207C10.1899 5.24155 10.1899 4.60838 10.5804 4.21786L13.0554 1.74286C13.0664 1.73194 13.0775 1.72129 13.0889 1.71089C14.3269 0.582536 15.952 -0.0254256 17.6266 0.0133431C19.3012 0.0521119 20.8965 0.73463 22.0809 1.91907C23.2654 3.1035 23.9479 4.69877 23.9867 6.37337C24.0254 8.04796 23.4175 9.67311 22.2891 10.9111C22.2783 10.9229 22.2673 10.9345 22.2559 10.9458L18.721 14.4682C18.7205 14.4687 18.7201 14.4691 18.7197 14.4696C18.1172 15.0739 17.4014 15.5535 16.6133 15.8808C15.8246 16.2084 14.979 16.377 14.125 16.377C13.2711 16.377 12.4255 16.2084 11.6368 15.8808C10.8481 15.5533 10.1319 15.0732 9.52912 14.4682C9.13933 14.077 9.14051 13.4438 9.53177 13.054C9.92302 12.6642 10.5562 12.6654 10.946 13.0567C11.3629 13.4752 11.8583 13.8072 12.4039 14.0338C12.9494 14.2603 13.5343 14.377 14.125 14.377C14.7158 14.377 15.3007 14.2603 15.8462 14.0338C16.3918 13.8072 16.8872 13.4752 17.3041 13.0567L17.3067 13.0541L20.8263 9.54692C21.5983 8.69126 22.0139 7.57237 21.9872 6.41966C21.9603 5.25933 21.4874 4.15397 20.6667 3.33328C19.846 2.51259 18.7407 2.03967 17.5804 2.01281C16.4274 1.98611 15.3082 2.40189 14.4525 3.17426Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.87508 9.62296C9.28437 9.62296 8.69946 9.73959 8.15392 9.96616C7.60837 10.1927 7.11293 10.5248 6.69601 10.9433L6.69344 10.9458L3.17381 14.453C2.40181 15.3087 1.98624 16.4276 2.01293 17.5803C2.03979 18.7406 2.51271 19.846 3.3334 20.6667C4.15409 21.4874 5.25945 21.9603 6.41978 21.9871C7.57277 22.0138 8.69193 21.598 9.54766 20.8257L12.0055 18.3679C12.396 17.9773 13.0292 17.9773 13.4197 18.3679C13.8102 18.7584 13.8102 19.3916 13.4197 19.7821L10.9447 22.2571C10.9338 22.268 10.9226 22.2786 10.9112 22.289C9.67323 23.4174 8.04809 24.0254 6.37349 23.9866C4.69889 23.9478 3.10362 23.2653 1.91919 22.0809C0.734752 20.8964 0.0522339 19.3012 0.0134652 17.6266C-0.0253035 15.952 0.582658 14.3268 1.71101 13.0888C1.72181 13.077 1.73288 13.0654 1.74423 13.0541L5.27916 9.53169C5.27961 9.53124 5.28006 9.53079 5.28051 9.53033C5.88296 8.92598 6.59873 8.44641 7.38682 8.11911C8.1755 7.79157 9.02109 7.62296 9.87508 7.62296C10.7291 7.62296 11.5747 7.79157 12.3633 8.11911C13.152 8.44666 13.8683 8.9267 14.471 9.53169C14.8608 9.92295 14.8596 10.5561 14.4684 10.9459C14.0771 11.3357 13.444 11.3345 13.0542 10.9433C12.6372 10.5248 12.1418 10.1927 11.5963 9.96616C11.0507 9.73959 10.4658 9.62296 9.87508 9.62296Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

5
src/icons/question.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 5C9.92487 5 5 9.92487 5 16C5 22.0751 9.92487 27 16 27C22.0751 27 27 22.0751 27 16C27 9.92487 22.0751 5 16 5ZM3 16C3 8.8203 8.8203 3 16 3C23.1797 3 29 8.8203 29 16C29 23.1797 23.1797 29 16 29C8.8203 29 3 23.1797 3 16Z" fill="#000022"/>
<path d="M16 24C16.8284 24 17.5 23.3284 17.5 22.5C17.5 21.6716 16.8284 21 16 21C15.1716 21 14.5 21.6716 14.5 22.5C14.5 23.3284 15.1716 24 16 24Z" fill="#000022"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.2779 9.34254C15.1002 9.00195 16.005 8.91283 16.8779 9.08647C17.7508 9.2601 18.5526 9.68869 19.182 10.318C19.8113 10.9474 20.2399 11.7492 20.4135 12.6221C20.5872 13.495 20.4981 14.3998 20.1575 15.2221C19.8169 16.0443 19.2401 16.7471 18.5001 17.2416C18.0413 17.5482 17.5325 17.7661 17 17.8875V18C17 18.5523 16.5523 19 16 19C15.4477 19 15 18.5523 15 18V17C15 16.7348 15.1054 16.4804 15.2929 16.2929C15.4804 16.1054 15.7348 16 16 16C16.4945 16 16.9778 15.8534 17.3889 15.5787C17.8 15.304 18.1205 14.9135 18.3097 14.4567C18.4989 13.9999 18.5484 13.4972 18.452 13.0123C18.3555 12.5273 18.1174 12.0819 17.7678 11.7322C17.4181 11.3826 16.9727 11.1445 16.4877 11.048C16.0028 10.9516 15.5001 11.0011 15.0433 11.1903C14.5865 11.3795 14.196 11.7 13.9213 12.1111C13.6466 12.5222 13.5 13.0055 13.5 13.5C13.5 14.0523 13.0523 14.5 12.5 14.5C11.9477 14.5 11.5 14.0523 11.5 13.5C11.5 12.61 11.7639 11.74 12.2584 10.9999C12.7529 10.2599 13.4557 9.68314 14.2779 9.34254Z" fill="#000022"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client';
import { ConfigProvider } from 'antd'
import { Theme } from './theme'
import { ThemeProvider } from './theme/ThemeProvider'
import { BrowserRouter } from "react-router-dom";
import App from './App';
import 'typeface-lato'
import 'antd/dist/reset.css';
@ -13,10 +14,12 @@ const root = ReactDOM.createRoot(
);
root.render(
<React.StrictMode>
<ConfigProvider theme={Theme}>
<ThemeProvider>
<App />
</ThemeProvider>
</ConfigProvider>
<BrowserRouter>
<ConfigProvider theme={Theme}>
<ThemeProvider>
<App />
</ThemeProvider>
</ConfigProvider>
</BrowserRouter>
</React.StrictMode>
);

View File

@ -0,0 +1,20 @@
import React, { FC } from 'react'
import { StyledContainer, StyledImageWrapper, StyledMainContent, StyledTitle, StyledContent } from './styled'
import { Divider } from 'antd'
import { Button } from '../../../components/Button'
export const Home: FC = () => {
return (
<StyledContainer>
<StyledImageWrapper />
<StyledMainContent>
<StyledTitle level={3}>Welcome to iHealth CheckMeSafe!</StyledTitle>
<StyledContent>
<Button block href="/activate">Activate Kit</Button>
<Divider plain>Or</Divider>
<Button block type="default" href="/login">Log in</Button>
</StyledContent>
</StyledMainContent>
</StyledContainer>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -0,0 +1 @@
export * from './Home'

View File

@ -0,0 +1,57 @@
import React from 'react'
import styled from 'styled-components'
import Image from './images/homePic@2x.png'
import { Title } from '../../../components/Typography'
export const StyledContainer = styled.div`
display: flex;
flex-direction: row;
padding: 25px 0 25px 40px;
${props => props.theme.breakpoints.down('s')} {
padding: 4px 23px;
flex-direction: column;
align-items: center;
}
`
export const StyledImageWrapper = styled.div`
width: 54%;
max-width: 737px;
height: 773px;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url(${Image});
flex: 0 0 auto;
${props => props.theme.breakpoints.down('s')} {
width: 100%;
height: 393px;
max-width: 344px;
}
`
export const StyledMainContent = styled.div`
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
flex: auto;
${props => props.theme.breakpoints.down('s')} {
width: 100%;
padding-top: 26px;
}
`
export const StyledTitle = styled(Title)`
`
export const StyledContent = styled.div`
margin-top: 44px;
width: 358px;
${props => props.theme.breakpoints.down('s')} {
width: 100%;
margin-top: 26px;
}
`

View File

@ -0,0 +1,209 @@
import React, { FC, useState } from 'react'
import styled from 'styled-components'
import { Modal, ModalProps } from '../../../components/Modal'
import { Title, Paragraph, Link } from '../../../components/Typography'
import { Form } from 'antd'
import { Button } from '../../../components/Button'
import { Input, Password, ErrorMessage, LabelWithTooltip } from '../../../components/FormControl'
import Image from './images/password@2x.png'
// TODO: Captcha
type Props = Pick<ModalProps, 'open'> & {
onClose?: () => void
}
const StyledWrapper = styled.div`
display: flex;
align-items: center;
flex-direction: column;
min-height: 366px;
${props => props.theme.breakpoints.up('s')} {
justify-content: center;
}
`
const StyledMain = styled.div`
width: 100%;
${props => props.theme.breakpoints.up('s')} {
width: 350px;
}
${props => props.theme.breakpoints.down('s')} {
padding-top: 37px;
}
.ant-form-item-required {
width: 100%;
}
`
const StyledTitle = styled(Title)`
margin-bottom: 2px !important;
${props => props.theme.breakpoints.down('s')} {
margin-bottom: 5px !important;
}
`
const StyledCaptchaHint = styled(Paragraph)`
margin-top: 30px;
`
const TitleCopies = [
'Forgot Password',
'Reset Password',
'Reset Password',
'Password Reset'
]
const DescriptionCopies = [
'Enter your email address to reset your password.',
'A 6-digit code has been sent to John.smith@gmail.com',
]
export const ForgetPasswordModal: FC<Props> = ({ onClose, ...props }) => {
const [step, setStep] = useState(0)
const [email, setEmail] = useState('')
const [captcha, setCaptcha] = useState('')
const [expiration, setExpiration] = useState('9:45')
const handleFinishEmail = ({ email }: { email: string }) => {
setEmail(email)
setStep(1)
}
const handleFinishCaptcha = ({ captcha }: { captcha: string }) => {
setCaptcha(captcha)
setStep(2)
}
const handleSendAgain = () => {
// TODO: Update expiration
}
const handleConfirm = (data: { password: string }) => {
console.log(data)
setStep(3)
}
const handleClose = () => {
setStep(0)
setEmail('')
setCaptcha('')
onClose && onClose()
}
const handleLogin = () => {
// TODO: login
}
return (
<Modal {...props} width={688} destroyOnClose onCancel={handleClose}>
<StyledWrapper>
<StyledMain>
<StyledTitle level={2}>{TitleCopies[step]}</StyledTitle>
{step < 2 && <Paragraph>{DescriptionCopies[step]}</Paragraph>}
{step === 0 ? (
<Form
layout="vertical"
style={{ marginTop: 4 }}
onFinish={handleFinishEmail}>
<Form.Item
name="email"
rules={[
{
required: true, message: <ErrorMessage message="Please input your email" />
},
{ type: 'email', message: <ErrorMessage message="Invalid email" /> }
]}
>
<Input placeholder="email@example.com" />
</Form.Item>
<Form.Item>
<Button style={{ marginTop: 20 }} block htmlType="submit">Next</Button>
</Form.Item>
</Form>
) : null}
{step === 1 ? (
<Form
layout="vertical"
style={{ marginTop: 27 }}
onFinish={handleFinishCaptcha}>
<Form.Item
name="captcha"
rules={[
{
required: true, message: <ErrorMessage message="Please input your code" />
},
]}
>
<Input />
</Form.Item>
<StyledCaptchaHint>
The code will expire in {expiration}<br />
Didnt receive the code? <Link onClick={handleSendAgain}>Send again</Link>
</StyledCaptchaHint>
<Form.Item>
<Button block htmlType="submit">Verify</Button>
</Form.Item>
</Form>
) : null}
{step === 2 ? (
<Form
layout="vertical"
onFinish={handleConfirm}
>
<Form.Item
label={
<LabelWithTooltip
primary
title='The minimum password length is 8 characters and must contain at least 1 uppercase letter, 1 lowercase letter, 1 number, and 1 special character (!@#$%^&*).'
>
Please enter a new password
</LabelWithTooltip>
}
name="password"
rules={[
{ required: true, message: <ErrorMessage message="Please input your password" /> },
{
pattern: /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/,
message: <ErrorMessage message="Invalid password" />
}
]}
>
<Password placeholder="********" />
</Form.Item>
<Form.Item
label="Confirm password"
name="repeatPassword"
dependencies={['password']}
rules={[{ required: true, message: <ErrorMessage message="Please confirm your password" /> },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('The two passwords that you entered do not match'));
},
}),
]}
>
<Password placeholder="********" />
</Form.Item>
<Form.Item>
<Button style={{ marginTop: 17 }} block htmlType="submit">Confirm</Button>
</Form.Item>
</Form>
) : null}
{step === 3 ? (
<>
<Paragraph style={{ marginTop: 20, marginBottom: 16 }}>
Your password has just been reset, please log in to continue.
</Paragraph>
<img src={Image} style={{ width: 232, height: 83, margin: 'auto', display: 'block' }} />
<Button block style={{ marginTop: 20 }} onClick={handleLogin}>Log in</Button>
</>
) : null}
</StyledMain>
</StyledWrapper>
</Modal>
)
}

View File

@ -0,0 +1,75 @@
import React, { FC, useState } from 'react'
import {
StyledContainer,
StyledImageWrapper,
StyledMainContent,
StyledHeadline,
StyledContent,
StyledButton,
StyledTitle, StyledHint, StyledHelp
} from './styled'
import { Form } from 'antd'
import { Paragraph, Link } from '../../../components/Typography'
import { Input, Password, ErrorMessage } from '../../../components/FormControl'
import { ForgetPasswordModal } from './ForgetPasswordModal'
export const Login: FC = () => {
const [showModal, setModal] = useState(false)
const handleShowModal = () => {
setModal(true)
}
const handleCloseModal = () => {
setModal(false)
}
const onFinish = (values: any) => {
// TODO: login
console.log('Success:', values);
};
return (
<StyledContainer>
<StyledImageWrapper />
<StyledMainContent>
<StyledHeadline level={1} style={{ fontSize: 36 }}>Welcome Back!</StyledHeadline>
<StyledTitle level={3}>Log in to continue</StyledTitle>
<StyledContent>
<Form
layout="vertical"
onFinish={onFinish}
>
<Form.Item
label="Email"
name="email"
rules={[
{
required: true, message: <ErrorMessage message="Please input your email" />
},
{ type: 'email', message: <ErrorMessage message="Invalid email" /> }
]}
>
<Input placeholder="email@example.com" />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: <ErrorMessage message="Please input your password" /> }]}
extra={<StyledHelp type="text" onClick={handleShowModal}>Forgot password</StyledHelp>}
>
<Password placeholder="********" />
</Form.Item>
<Form.Item>
<StyledButton block htmlType="submit">Log in</StyledButton>
</Form.Item>
</Form>
<StyledHint>
<Paragraph>
Dont have an account?&nbsp;
<Link href="/activate">Activate your kit</Link>
</Paragraph>
</StyledHint>
</StyledContent>
</StyledMainContent>
<ForgetPasswordModal open={showModal} onClose={handleCloseModal} />
</StyledContainer>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1 @@
export * from './Login'

View File

@ -0,0 +1,80 @@
import React from 'react'
import styled from 'styled-components'
import Image from './images/loginPic@2x.png'
import { Title } from '../../../components/Typography'
import { Button } from '../../../components/Button'
export const StyledContainer = styled.div`
display: flex;
flex-direction: row;
padding: 25px 0 25px 40px;
${props => props.theme.breakpoints.down('s')} {
padding: 16px;
flex-direction: column;
align-items: center;
}
`
export const StyledImageWrapper = styled.div`
width: 54%;
max-width: 737px;
height: 803px;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url(${Image});
flex: 0 0 auto;
${props => props.theme.breakpoints.down('s')} {
display: none;
}
`
export const StyledMainContent = styled.div`
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
flex: auto;
${props => props.theme.breakpoints.down('s')} {
width: 100%;
padding-top: 50px;
}
`
export const StyledHeadline = styled(Title)`
margin-bottom: 10px !important;
font-size: 36px;
${props => props.theme.breakpoints.down('s')} {
margin-bottom: 4px !important;
}
`
export const StyledTitle = styled(Title)`
margin-top: 0 !important;
`
export const StyledContent = styled.div`
margin-top: 52px;
width: 358px;
${props => props.theme.breakpoints.down('s')} {
width: 100%;
margin-top: 47px;
}
`
export const StyledButton = styled(Button)`
margin-top: 30px;
`
export const StyledHelp = styled(Button)`
padding: 0;
font-size: 16px;
`
export const StyledHint = styled.div`
text-align: center;
margin-top: 33px;
`

View File

@ -18,6 +18,7 @@ html, body {
.ant-btn-link, .ant-btn-text {
font-size: 12px;
line-height: 16px;
height: 16px;
}
.ant-wave {
@ -48,6 +49,7 @@ html, body {
}
.health-tooltip-highlighted.ant-tooltip .ant-tooltip-inner {
display: inline-flex;
position: relative;
box-shadow: 1px 1px 0px 0 #FF5A0C, -1px -1px 0px 0 #FF5A0C, -1px 1px 0px 0 #FF5A0C, 1px -1px 0px 0 #FF5A0C;
}
@ -58,6 +60,7 @@ html, body {
.health-tooltip.ant-tooltip .ant-tooltip-inner {
position: relative;
display: inline-flex;
box-shadow: 0 2px 0px 0 #000022, 0 2px 0px 0px #000022, 1px 1px 0px 2px #000022;
}
@ -191,3 +194,27 @@ html, body {
.ant-form-item {
margin-bottom: 15px;
}
@media (min-width: 500px) {
.ant-form-item {
margin-bottom: 11px;
}
}
@media (max-width: 500px) {
.ant-form-item .ant-form-item-explain-error {
font-size: 10px;
}
.ant-modal .ant-modal-content {
padding-left: 18px;
padding-right: 18px;
}
}
.ant-form-item .ant-form-item-label > label .ant-form-item-tooltip {
color: #000022;
}

View File

@ -1,4 +1,5 @@
import { ThemeConfig } from 'antd'
export const breakpointList = {
xs: 0,
s: 500,
@ -53,6 +54,11 @@ export const Theme: ThemeConfig = {
colorBgTextHover: 'transparent',
colorBgTextActive: 'transparent',
},
Divider: {
colorSplit: '#000',
colorText: '#000',
fontSize: 20,
},
Modal: {
borderRadiusLG: 10,
boxShadow: '2px 2px 4px 1px rgba(0, 0, 0, 0.2)',