diff --git a/src/modules/mindset/components/Icon/ChevronRight.tsx b/src/modules/mindset/components/Icon/ChevronRight.tsx new file mode 100644 index 000000000..e6f7e4aaa --- /dev/null +++ b/src/modules/mindset/components/Icon/ChevronRight.tsx @@ -0,0 +1,13 @@ +/* eslint-disable */ +import React from 'react' + +export const ChevronRight: React.FC> = (props) => ( + + + +) + +export default ChevronRight diff --git a/src/modules/mindset/components/header/Icon/Logo.tsx b/src/modules/mindset/components/Icon/Logo.tsx similarity index 100% rename from src/modules/mindset/components/header/Icon/Logo.tsx rename to src/modules/mindset/components/Icon/Logo.tsx diff --git a/src/modules/mindset/components/LandingPage/index.tsx b/src/modules/mindset/components/LandingPage/index.tsx new file mode 100644 index 000000000..8e61f31fb --- /dev/null +++ b/src/modules/mindset/components/LandingPage/index.tsx @@ -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) => { + const { value } = e.target + + setInputValue(value) + setError(value !== '' && !isValidMediaUrl(value)) + } + + const handleSubmit = () => { + if (isValidMediaUrl(inputValue)) { + console.log('Valid media URL:', inputValue) + } + } + + return ( + + Ideas have shapes + + e.key === 'Enter' && handleSubmit()} + placeholder="Paste podcast or video link" + value={inputValue} + /> + + + + + + ) +} + +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}; + } +` diff --git a/src/modules/mindset/components/LandingPage/utils/index.tsx b/src/modules/mindset/components/LandingPage/utils/index.tsx new file mode 100644 index 000000000..2c5b80d9a --- /dev/null +++ b/src/modules/mindset/components/LandingPage/utils/index.tsx @@ -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)) +} diff --git a/src/modules/mindset/components/header/index.tsx b/src/modules/mindset/components/header/index.tsx index 5feb22db4..5b633e8f4 100644 --- a/src/modules/mindset/components/header/index.tsx +++ b/src/modules/mindset/components/header/index.tsx @@ -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 = () => ( @@ -12,6 +13,9 @@ export const Header = () => ( Graph Mindset + + + ) @@ -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)` @@ -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; + } +` diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index ead6335a5..d299fd20e 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -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 = () => (
+ ) diff --git a/src/utils/colors/index.tsx b/src/utils/colors/index.tsx index 47f7ce070..38cbea411 100644 --- a/src/utils/colors/index.tsx +++ b/src/utils/colors/index.tsx @@ -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