Skip to content

Commit

Permalink
Merge pull request #2467 from MuhammadUmer44/mindset-landing-page
Browse files Browse the repository at this point in the history
[Graph Mindset] Landing Page
  • Loading branch information
Rassl authored Nov 23, 2024
2 parents f85d3dc + e8e1aad commit 4379ec1
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 1 deletion.
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)
}
}

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

0 comments on commit 4379ec1

Please sign in to comment.