Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Graph Mindset] Landing Page #2467

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/modules/mindset/components/Icon/ChevronRight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable */
import React from 'react'

export const ChevronRight: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="1em" height="1em" viewBox="0 0 7 12" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.77978 6.00243L0.706705 1.92933C0.568239 1.79088 0.497405 1.61684 0.494205 1.40723C0.490989 1.19763 0.561822 1.02039 0.706705 0.875528C0.851572 0.730645 1.02721 0.658203 1.23361 0.658203C1.44001 0.658203 1.61564 0.730645 1.7605 0.875528L6.25473 5.36975C6.34833 5.46334 6.41436 5.56205 6.45281 5.6659C6.49127 5.76974 6.51051 5.88191 6.51051 6.00243C6.51051 6.12294 6.49127 6.23512 6.45281 6.33895C6.41436 6.4428 6.34833 6.54152 6.25473 6.6351L1.7605 11.1293C1.62205 11.2678 1.44802 11.3386 1.2384 11.3418C1.0288 11.345 0.851572 11.2742 0.706705 11.1293C0.561822 10.9845 0.48938 10.8088 0.48938 10.6024C0.48938 10.396 0.561822 10.2204 0.706705 10.0755L4.77978 6.00243Z"
fill="currentColor"
/>
</svg>
)

export default ChevronRight
113 changes: 113 additions & 0 deletions src/modules/mindset/components/LandingPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { useState } from 'react'
import styled from 'styled-components'
import { Flex } from '~/components/common/Flex'
import { colors } from '~/utils/colors'
import { ChevronRight } from '../Icon/ChevronRight'
import { isValidMediaUrl } from './utils'

export const LandingPage = () => {
const [inputValue, setInputValue] = useState('')
const [error, setError] = useState(false)

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target

setInputValue(value)
setError(value !== '' && !isValidMediaUrl(value))
}

const handleSubmit = () => {
if (isValidMediaUrl(inputValue)) {
console.log('Valid media URL:', inputValue)

Check warning on line 21 in src/modules/mindset/components/LandingPage/index.tsx

View workflow job for this annotation

GitHub Actions / craco-build-run

Unexpected console statement
}
}

return (
<Wrapper>
<Title>Ideas have shapes</Title>
<InputWrapper>
<Input
error={error}
onChange={handleInputChange}
onKeyDown={(e) => e.key === 'Enter' && handleSubmit()}
placeholder="Paste podcast or video link"
value={inputValue}
/>
<IconWrapper error={error} onClick={!error ? handleSubmit : undefined}>
<ChevronRight />
</IconWrapper>
</InputWrapper>
</Wrapper>
)
}

const Wrapper = styled(Flex)`
background: #16161de3;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
color: #fff;
align-items: center;
justify-content: center;
font-size: 32px;
font-style: normal;
font-weight: 700;
line-height: 16px;
font-family: 'Barlow';
z-index: 40;
`

const Title = styled(Flex)`
color: ${colors.white};
font-family: Barlow;
font-size: 32px;
font-weight: 700;
margin-bottom: 40px;
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
`

const Input = styled.input<{ error?: boolean }>`
width: 100%;
max-width: 450px;
padding: 12px 28px 12px 16px;
border-radius: 100px;
border: 1px solid ${(props) => (props.error ? 'red' : colors.DIVIDER_4)};
background: ${colors.INPUT_BG};
color: ${colors.white};
font-family: Barlow;
font-size: 16px;

&::placeholder {
color: ${colors.INPUT_PLACEHOLDER};
}

&:focus {
outline: none;
border-color: ${(props) => (props.error ? 'red' : colors.primaryBlue)};
}
`

const InputWrapper = styled.div`
position: relative;
width: 450px;
display: flex;
align-items: center;
`

const IconWrapper = styled.div<{ error?: boolean }>`
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
color: ${colors.white};
font-size: 20px;
cursor: ${(props) => (props.error ? 'not-allowed' : 'pointer')};

svg {
width: 8px;
height: 17px;
color: ${colors.GRAY6};
}
`
47 changes: 47 additions & 0 deletions src/modules/mindset/components/LandingPage/utils/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const protocol = /^(https?:\/\/)/
const subDomain = /(www\.)?/
const rootDomain = /[\w-]+(\.[\w-]+)*/
const topLevelDomains = /(?:\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61})[a-zA-Z0-9](?:\.[a-zA-Z]{2,})/
const path = /(\/[^\s?]*)?/
const query = /(\?[^\s]*)?/

const youtubeRegex = /(https?:\/\/)?(www\.)?youtube\.com\/watch\?v=([A-Za-z0-9_-]+)/
const youtubeLiveRegex = /(https?:\/\/)?(www\.)?youtube\.com\/live\/([A-Za-z0-9_-]+)/
const youtubeShortRegex = /(https?:\/\/)?(www\.)?youtu\.be\/([A-Za-z0-9_-]+)/
const mp3Regex = /(https?:\/\/)?([A-Za-z0-9_-]+)\.mp3/

const urlRegex = new RegExp(
`${protocol.source}${subDomain.source}${rootDomain.source}${topLevelDomains.source}?${path.source}${query.source}$`,
'i',
)

export const validateUrl = (input: string): boolean => {
try {
const match = input?.match(urlRegex)

if (!match) {
return false
}

const url = new URL(input)
const domain = url.hostname

if (domain?.startsWith('www.')) {
return (domain?.match(/\./g) || []).length >= 2
}

return (domain?.match(/\./g) || []).length >= 1
} catch {
return false
}
}

export const isValidMediaUrl = (url: string): boolean => {
if (!validateUrl(url)) {
return false
}

const mediaPatterns = [youtubeRegex, youtubeLiveRegex, youtubeShortRegex, mp3Regex]

return mediaPatterns.some((pattern) => pattern.test(url))
}
35 changes: 34 additions & 1 deletion src/modules/mindset/components/header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import styled from 'styled-components'
import { Flex } from '~/components/common/Flex'
import { Text } from '~/components/common/Text'
import ClearIcon from '~/components/Icons/ClearIcon'
import { colors } from '~/utils/colors'
import { Logo } from './Icon/Logo'
import { Logo } from '../Icon/Logo'

export const Header = () => (
<Head>
Expand All @@ -12,6 +13,9 @@ export const Header = () => (
</IconWrapper>
</LogoButton>
<StyledText>Graph Mindset</StyledText>
<CloseButton>
<ClearIcon />
</CloseButton>
</Head>
)

Expand All @@ -24,6 +28,8 @@ const Head = styled(Flex).attrs({
height: 64px;
padding: 20px 23px;
gap: 0px;
z-index: 50;
position: relative;
`

const LogoButton = styled(Flex)`
Expand Down Expand Up @@ -57,3 +63,30 @@ const StyledText = styled(Text)`
margin-left: 16px;
white-space: nowrap;
`

const CloseButton = styled.div`
position: absolute;
right: 16px;
top: 40%;
transform: translateY(-50%);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
background: ${colors.BUTTON1};
transition: background 0.2s;

&:hover {
background: ${colors.BUTTON1_PRESS};
}

svg {
fill: none;
color: ${colors.white} !important;
width: 16px;
height: 16px;
}
`
2 changes: 2 additions & 0 deletions src/modules/mindset/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Flex } from '~/components/common/Flex'
import { Header } from './components/header'
import { LandingPage } from './components/LandingPage'
import { SideBar } from './components/sidebar'

export const MindSet = () => (
<Flex>
<Header />
<LandingPage />
<SideBar />
</Flex>
)
3 changes: 3 additions & 0 deletions src/utils/colors/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export const colors = {
createTestButton: 'rgb(178, 255, 102)',
MESSAGE_BG: 'rgba(22, 22, 29, 0.89)',
MESSAGE_BG_HOVER: 'rgba(35, 37, 47, 0.3)',
DIVIDER_4: 'rgba(46, 55, 67, 1)',
INPUT_BG: 'rgba(255, 255, 255, 0.05)',
INPUT_PLACEHOLDER: 'rgba(255, 255, 255, 0.5)',
} as const

export type ColorName = keyof typeof colors
Loading