mirror of
https://github.com/vernu/textbee.git
synced 2026-05-18 05:14:49 -04:00
landing page improvement and code refactor
This commit is contained in:
@@ -16,8 +16,8 @@ async function bootstrap() {
|
||||
})
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('VERNU SMS Gateway api docs')
|
||||
.setDescription('api docs')
|
||||
.setTitle('TextBee API Docs')
|
||||
.setDescription('TextBee - Android SMS Gateway API Docs')
|
||||
.setVersion('1.0')
|
||||
.addBearerAuth()
|
||||
.build()
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
NEXT_PUBLIC_API_BASE_URL=https://api.sms.vernu.dev/api/v1
|
||||
NEXT_PUBLIC_API_BASE_URL=https://api.textbee.vernu.dev/api/v1
|
||||
NEXT_PUBLIC_GOOGLE_CLIENT_ID=
|
||||
52
web/components/Footer.tsx
Normal file
52
web/components/Footer.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
Box,
|
||||
chakra,
|
||||
Container,
|
||||
Stack,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<Box
|
||||
bg={useColorModeValue('gray.50', 'gray.900')}
|
||||
color={useColorModeValue('gray.700', 'gray.200')}
|
||||
>
|
||||
<Container
|
||||
as={Stack}
|
||||
maxW={'6xl'}
|
||||
py={4}
|
||||
spacing={4}
|
||||
justify={'center'}
|
||||
align={'center'}
|
||||
>
|
||||
<Stack direction={'row'} spacing={6}>
|
||||
<Link href='/'>Home</Link>
|
||||
<Link href='/dashboard'>Dashboard</Link>
|
||||
<Link href='/android'>Download App</Link>
|
||||
<Link href='https://github.com/vernu/textbee'>Github</Link>
|
||||
</Stack>
|
||||
</Container>
|
||||
|
||||
<Box
|
||||
borderTopWidth={1}
|
||||
borderStyle={'solid'}
|
||||
borderColor={useColorModeValue('gray.200', 'gray.700')}
|
||||
>
|
||||
<Container
|
||||
as={Stack}
|
||||
maxW={'6xl'}
|
||||
py={4}
|
||||
direction={{ base: 'column', md: 'row' }}
|
||||
spacing={4}
|
||||
justify='center'
|
||||
align={{ base: 'center', md: 'center' }}
|
||||
>
|
||||
<Text>© {new Date().getFullYear()} All rights reserved</Text>
|
||||
</Container>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -27,7 +27,12 @@ export default function Navbar() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box bg={useColorModeValue('gray.100', 'gray.700')} px={4} shadow='lg' mb={1}>
|
||||
<Box
|
||||
bg={useColorModeValue('gray.100', 'blue.600')}
|
||||
px={4}
|
||||
shadow='lg'
|
||||
mb={1}
|
||||
>
|
||||
<Flex h={16} alignItems={'center'} justifyContent={'space-between'}>
|
||||
<Link href='/' passHref>
|
||||
<Flex alignItems={'center'}>
|
||||
@@ -37,9 +42,10 @@ export default function Navbar() {
|
||||
w={'30px'}
|
||||
h={'30px'}
|
||||
src={'/images/sms-gateway-logo.png'}
|
||||
borderRadius='full'
|
||||
/>
|
||||
<Box style={{ cursor: 'pointer', marginLeft: '5px' }}>
|
||||
VERNU SMS
|
||||
TextBee
|
||||
</Box>
|
||||
</Flex>
|
||||
</Link>
|
||||
@@ -50,6 +56,12 @@ export default function Navbar() {
|
||||
{colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
|
||||
</Button>
|
||||
|
||||
<Menu>
|
||||
<Link href='https://github.com/vernu/textbee' passHref>
|
||||
<MenuButton>Github</MenuButton>
|
||||
</Link>
|
||||
</Menu>
|
||||
|
||||
{!user ? (
|
||||
<>
|
||||
<Menu>
|
||||
@@ -102,7 +114,6 @@ export default function Navbar() {
|
||||
>
|
||||
Dashboard
|
||||
</MenuItem>
|
||||
<MenuItem>Account Settings</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
dispatch(logout())
|
||||
|
||||
@@ -64,20 +64,13 @@ const DeviceList = () => {
|
||||
<Tr key={_id}>
|
||||
<Td>{`${brand}/ ${model}`}</Td>
|
||||
<Td>{enabled ? 'enabled' : 'disabled'}</Td>
|
||||
<Td>
|
||||
<EmailIcon onDoubleClick={(e) => {}} />
|
||||
</Td>
|
||||
<Td>{/* <EmailIcon onDoubleClick={(e) => {}} /> */}</Td>
|
||||
<Td>
|
||||
<Tooltip label='Double Click to delete'>
|
||||
<IconButton
|
||||
aria-label='Delete'
|
||||
icon={<DeleteIcon />}
|
||||
onDoubleClick={(e) => {
|
||||
sendSMSRequest(_id, {
|
||||
receivers: ['+251912657519'],
|
||||
smsBody: 'Hello World',
|
||||
})
|
||||
}}
|
||||
onDoubleClick={(e) => {}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Td>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
chakra,
|
||||
Flex,
|
||||
@@ -112,32 +113,40 @@ export default function GenerateApiKey() {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{' '}
|
||||
<Flex justifyContent='center'>
|
||||
<Button
|
||||
/* flex={1} */
|
||||
px={4}
|
||||
fontSize={'sm'}
|
||||
rounded={'full'}
|
||||
bg={'blue.400'}
|
||||
color={'white'}
|
||||
boxShadow={
|
||||
'0px 1px 25px -5px rgb(66 153 225 / 48%), 0 10px 10px -5px rgb(66 153 225 / 43%)'
|
||||
}
|
||||
_hover={{
|
||||
bg: 'blue.500',
|
||||
}}
|
||||
_focus={{
|
||||
bg: 'blue.500',
|
||||
}}
|
||||
onClick={generateApiKey}
|
||||
disabled={generatingApiKey}
|
||||
>
|
||||
{generatingApiKey
|
||||
? 'generating... '
|
||||
: 'Generate Api Key/ Register Device'}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box padding={5} border='1px solid gray' marginBottom={10} borderRadius='2xl'>
|
||||
<Flex direction='row' justifyContent='space-between'>
|
||||
{' '}
|
||||
<chakra.h1
|
||||
fontSize='md'
|
||||
fontWeight='bold'
|
||||
mt={2}
|
||||
color={useColorModeValue('gray.800', 'white')}
|
||||
>
|
||||
Generate Api Key and Register Device
|
||||
</chakra.h1>
|
||||
<Button
|
||||
/* flex={1} */
|
||||
px={4}
|
||||
fontSize={'sm'}
|
||||
rounded={'full'}
|
||||
bg={'blue.400'}
|
||||
color={'white'}
|
||||
boxShadow={
|
||||
'0px 1px 25px -5px rgb(66 153 225 / 48%), 0 10px 10px -5px rgb(66 153 225 / 43%)'
|
||||
}
|
||||
_hover={{
|
||||
bg: 'blue.500',
|
||||
}}
|
||||
_focus={{
|
||||
bg: 'blue.500',
|
||||
}}
|
||||
onClick={generateApiKey}
|
||||
disabled={generatingApiKey}
|
||||
>
|
||||
{generatingApiKey ? 'loading... ' : 'Get Started'}
|
||||
</Button>
|
||||
</Flex>{' '}
|
||||
</Box>
|
||||
{generatedApiKey && (
|
||||
<>
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
@@ -53,7 +54,11 @@ export default function SendSMS() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={onOpen}>Start Sending</Button>
|
||||
<Flex justifyContent='flex-end' marginBottom={20}>
|
||||
<Button bg={'blue.400'} color={'white'} onClick={onOpen}>
|
||||
Send SMS
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
import { Box, SimpleGrid, chakra } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { selectApiKeyList } from '../../store/apiKeyListReducer'
|
||||
import { selectAuth } from '../../store/authReducer'
|
||||
import { selectDeviceList } from '../../store/deviceListReducer'
|
||||
import UserStatsCard from './UserStatsCard'
|
||||
|
||||
const UserStats = () => {
|
||||
const { user: currentUser } = useSelector(selectAuth)
|
||||
|
||||
const { data: deviceListData } = useSelector(selectDeviceList)
|
||||
const { data: apiKeyListData } = useSelector(selectApiKeyList)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box maxW='7xl' mx={'auto'} pt={5} px={{ base: 2, sm: 12, md: 17 }}>
|
||||
<chakra.h1
|
||||
textAlign={'center'}
|
||||
fontSize={'4xl'}
|
||||
py={10}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
Welcome {currentUser?.name}
|
||||
</chakra.h1>
|
||||
<SimpleGrid columns={{ base: 1, md: 3 }} spacing={{ base: 5, lg: 8 }}>
|
||||
<UserStatsCard title={'Registered '} stat={'2 devices'} />
|
||||
<UserStatsCard title={'Generated'} stat={'3 API Keys'} />
|
||||
<UserStatsCard title={'Sent'} stat={'100 SMS'} />
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} >
|
||||
<chakra.h1
|
||||
textAlign={'center'}
|
||||
fontSize={'4xl'}
|
||||
py={10}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
Welcome {currentUser?.name}
|
||||
</chakra.h1>
|
||||
<SimpleGrid columns={{ base: 3 }} spacing={{ base: 5, lg: 8 }}>
|
||||
<UserStatsCard title={'Registered '} stat={`${deviceListData?.length || '-:-'} Devices`} />
|
||||
<UserStatsCard title={'Generated'} stat={`${apiKeyListData?.length || '-:-'} API Keys`} />
|
||||
<UserStatsCard title={'Sent'} stat={'-:- SMS'} />
|
||||
</SimpleGrid>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
</>
|
||||
|
||||
@@ -16,11 +16,14 @@ export default function UserStatsCard({ ...props }) {
|
||||
border={'1px solid'}
|
||||
borderColor={useColorModeValue('gray.800', 'gray.500')}
|
||||
rounded={'lg'}
|
||||
style={{
|
||||
height: '90px'
|
||||
}}
|
||||
>
|
||||
<StatLabel fontWeight={'medium'} isTruncated>
|
||||
{title}
|
||||
</StatLabel>
|
||||
<StatNumber fontSize={'2xl'} fontWeight={'medium'}>
|
||||
<StatNumber fontSize={'md'} fontWeight={'bold'}>
|
||||
{stat}
|
||||
</StatNumber>
|
||||
</Stat>
|
||||
|
||||
42
web/components/home/CodeSnippetSection.tsx
Normal file
42
web/components/home/CodeSnippetSection.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Box, Flex, Heading, Image, Text } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
export default function CodeSnippetSection() {
|
||||
return (
|
||||
<Box m={{ base: 0, md: 8 }} p={{ base: 0, md: 8 }}>
|
||||
<Flex
|
||||
height='100%'
|
||||
direction='column'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
>
|
||||
<Heading fontSize={'3xl'} textAlign={'center'} py={8}>
|
||||
Code Snippet
|
||||
</Heading>
|
||||
<Text color={'gray.600'} fontSize={'lg'} textAlign={'center'} pb='4'>
|
||||
Send SMS messages from your web application using our REST API. You
|
||||
can use any programming language to interact with our API. Here is a
|
||||
sample code snippet in JavaScript using axios library.
|
||||
</Text>
|
||||
|
||||
<Box
|
||||
borderRadius={'lg'}
|
||||
padding={{ base: 0, md: 8 }}
|
||||
border={'1px solid #E2E8F0'}
|
||||
w={{ base: '100%', md: '70%' }}
|
||||
>
|
||||
<Image
|
||||
alt={'Hero Image'}
|
||||
fit={'cover'}
|
||||
align={'center'}
|
||||
// h={'100%'}
|
||||
src={
|
||||
'https://ik.imagekit.io/vernu/textbee/Screenshot_2023-06-18_at_11.30.25_AM.png?updatedAt=1687077054749'
|
||||
}
|
||||
borderRadius={'lg'}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
88
web/components/home/DownloadAppSection.tsx
Normal file
88
web/components/home/DownloadAppSection.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
chakra,
|
||||
Flex,
|
||||
Image,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
export default function DownloadAppSection() {
|
||||
return (
|
||||
<Box my={16}>
|
||||
<Flex
|
||||
padding={5}
|
||||
background={useColorModeValue('gray.100', 'gray.700')}
|
||||
borderRadius='2xl'
|
||||
>
|
||||
<Flex
|
||||
borderRadius='2xl'
|
||||
m={{ base: 5, md: 8 }}
|
||||
p={{ base: 5, md: 8 }}
|
||||
width='100%'
|
||||
border='1px solid gray'
|
||||
direction='row'
|
||||
justifyContent='center'
|
||||
>
|
||||
<Box>
|
||||
<Image
|
||||
alt={'Hero Image'}
|
||||
fit={'cover'}
|
||||
align={'center'}
|
||||
w={'180px'}
|
||||
// h={'100%'}
|
||||
src={'/images/smsgatewayandroid.png'}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex
|
||||
height='100%'
|
||||
direction='column'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
>
|
||||
<chakra.h1
|
||||
fontSize='md'
|
||||
fontWeight='bold'
|
||||
my={4}
|
||||
color={useColorModeValue('gray.800', 'white')}
|
||||
>
|
||||
Download the App to get started!
|
||||
</chakra.h1>
|
||||
<chakra.p
|
||||
fontSize='sm'
|
||||
color={useColorModeValue('gray.600', 'gray.400')}
|
||||
mb={4}
|
||||
>
|
||||
Unlock the power of messaging with our open-source Android SMS
|
||||
Gateway.
|
||||
</chakra.p>
|
||||
<a href='/android' target='_blank'>
|
||||
<Button
|
||||
/* flex={1} */
|
||||
px={4}
|
||||
fontSize={'sm'}
|
||||
rounded={'full'}
|
||||
bg={'blue.400'}
|
||||
color={'white'}
|
||||
boxShadow={
|
||||
'0px 1px 25px -5px rgb(66 153 225 / 48%), 0 10px 10px -5px rgb(66 153 225 / 43%)'
|
||||
}
|
||||
_hover={{
|
||||
bg: 'blue.500',
|
||||
}}
|
||||
_focus={{
|
||||
bg: 'blue.500',
|
||||
}}
|
||||
>
|
||||
Download App
|
||||
</Button>
|
||||
</a>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -8,37 +8,46 @@ import {
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
VStack,
|
||||
} from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import { featuresContent } from './featuresContent'
|
||||
|
||||
const FeaturesSection = () => {
|
||||
return (
|
||||
<Box p={4}>
|
||||
<Stack spacing={4} as={Container} maxW={'6xl'}>
|
||||
<Heading fontSize={'3xl'} textAlign={'center'} pb={8}>
|
||||
Features
|
||||
</Heading>
|
||||
<Text color={'gray.600'} fontSize={'lg'} textAlign={'justify'}>
|
||||
The ultimate solution for your messaging needs! Our free open-source
|
||||
Android-based SMS Gateway provides you with all the features you need
|
||||
to effectively manage your SMS communications. From sending messages
|
||||
and automating messaging workflows via API, our SMS Gateway is the
|
||||
perfect tool for any small/mid business or individual.
|
||||
</Text>
|
||||
</Stack>
|
||||
export default function FeaturesSection() {
|
||||
const boxBgColor = useColorModeValue('gray.100', 'gray.800')
|
||||
|
||||
<Container maxW={'6xl'} mt={10}>
|
||||
return (
|
||||
<Box p={4} my={16} maxW={'6xl'}>
|
||||
<Heading fontSize={'3xl'} textAlign={'center'} pb={0}>
|
||||
Features
|
||||
</Heading>
|
||||
<Text color={'gray.600'} fontSize={'lg'} textAlign={'center'}>
|
||||
The ultimate solution for your messaging needs! Our free open-source
|
||||
Android-based SMS Gateway provides you with all the features you need to
|
||||
effectively manage your SMS communications. From sending messages and
|
||||
automating messaging workflows via API, our SMS Gateway is the perfect
|
||||
tool for any small/mid business or individual.
|
||||
</Text>
|
||||
|
||||
<Container maxW={'6xl'} mt={0}>
|
||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={3} pt={16}>
|
||||
{featuresContent.map((feature, i) => (
|
||||
<HStack key={i} align={'top'} borderWidth="1px" borderRadius="md" p={2} shadow='lg' >
|
||||
<HStack
|
||||
key={i}
|
||||
align={'top'}
|
||||
borderWidth='1px'
|
||||
borderRadius='sm'
|
||||
p={2}
|
||||
shadow='lg'
|
||||
background={boxBgColor}
|
||||
>
|
||||
<Box color={'green.400'} px={1}>
|
||||
<Icon as={CheckIcon} />
|
||||
</Box>
|
||||
<VStack align={'start'}>
|
||||
<Text fontWeight={600}>{feature.title}</Text>
|
||||
<Text color={'gray.600'}>{feature.description}</Text>
|
||||
<Text fontWeight={800}>{feature.title}</Text>
|
||||
<Text fontWeight='normal'>{feature.description}</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
))}
|
||||
@@ -47,5 +56,3 @@ const FeaturesSection = () => {
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeaturesSection
|
||||
|
||||
@@ -2,13 +2,11 @@ import { AddIcon, MinusIcon } from '@chakra-ui/icons'
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Box,
|
||||
Container,
|
||||
Heading,
|
||||
Stack,
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
@@ -16,22 +14,20 @@ import { howItWorksContent } from './howItWorksContent'
|
||||
|
||||
export default function HowItWorksSection() {
|
||||
return (
|
||||
<Box p={4}>
|
||||
<Stack spacing={4} as={Container} maxW={'6xl'}>
|
||||
<a id='#how-it-works'>
|
||||
<Heading fontSize={'3xl'} textAlign={'center'} py={8}>
|
||||
How It Works
|
||||
</Heading>
|
||||
</a>
|
||||
<Text color={'gray.600'} fontSize={'lg'} textAlign={'justify'}>
|
||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Illo
|
||||
exercitationem quo quibusdam, fugit quaerat odio quisquam commodi ut?
|
||||
Aliquid ab sapiente, expedita quas neque amet consectetur quisquam
|
||||
reprehenderit voluptas commodi?
|
||||
</Text>
|
||||
</Stack>
|
||||
<Box px={4} my={24} maxW={'6xl'}>
|
||||
{/* @ts-ignore */}
|
||||
<a name='how-it-works'>
|
||||
<Heading fontSize={'3xl'} textAlign={'center'}>
|
||||
How It Works
|
||||
</Heading>
|
||||
</a>
|
||||
<Text color={'gray.600'} fontSize={'lg'} textAlign={'center'}>
|
||||
How it works is simple. You install the app on your Android device, and
|
||||
it will turn your device into a SMS Gateway. You can then use the API to
|
||||
send SMS messages from your own applications.
|
||||
</Text>
|
||||
|
||||
<Container maxW={'6xl'} mt={10} pt={8}>
|
||||
<Container maxW={'6xl'} mt={10} pt={0}>
|
||||
<Accordion allowMultiple defaultIndex={[]}>
|
||||
{howItWorksContent.map(({ title, description }) => (
|
||||
<AccordionItem key={title}>
|
||||
|
||||
@@ -7,15 +7,27 @@ import {
|
||||
Text,
|
||||
Button,
|
||||
Image,
|
||||
IconButton,
|
||||
createIcon,
|
||||
} from '@chakra-ui/react'
|
||||
import Link from 'next/link'
|
||||
import Router from 'next/router'
|
||||
import { selectAuth } from '../../store/authReducer'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ChatIcon } from '@chakra-ui/icons'
|
||||
|
||||
export default function IntroSection() {
|
||||
const { currentUser } = useSelector(selectAuth)
|
||||
|
||||
const handleGetStarted = () => {
|
||||
if (!currentUser) {
|
||||
Router.push('/register')
|
||||
} else {
|
||||
Router.push('/dashboard')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container maxW={'7xl'}>
|
||||
<Container maxW={'7xl'} py={8}>
|
||||
<Stack
|
||||
align={'center'}
|
||||
spacing={{ base: 8, md: 10 }}
|
||||
@@ -28,28 +40,21 @@ export default function IntroSection() {
|
||||
fontWeight={600}
|
||||
fontSize={{ base: '3xl', sm: '4xl', lg: '5xl' }}
|
||||
>
|
||||
<Text
|
||||
as={'span'}
|
||||
position={'relative'}
|
||||
_after={{
|
||||
content: "''",
|
||||
width: 'full',
|
||||
height: '30%',
|
||||
position: 'absolute',
|
||||
bottom: 1,
|
||||
left: 0,
|
||||
bg: 'blue.400',
|
||||
zIndex: -1,
|
||||
}}
|
||||
>
|
||||
VERNU SMS Gateway,
|
||||
<Text as={'span'} position={'relative'} fontWeight={600}>
|
||||
<ChatIcon /> Text
|
||||
<Text as={'span'} color={'blue.400'} decoration='underline'>
|
||||
Bee
|
||||
</Text>
|
||||
</Text>
|
||||
<br />
|
||||
<Text as={'span'} color={'blue.400'}>
|
||||
<Text as={'span'} color={'blue.400'} fontWeight={300}>
|
||||
Make your android device a portable SMS Gateway!
|
||||
</Text>
|
||||
</Heading>
|
||||
<Text color={'gray.500'}>
|
||||
<Text
|
||||
color={'gray.500'}
|
||||
fontSize={{ base: 'md', sm: 'lg', lg: 'xl' }}
|
||||
>
|
||||
Unlock the power of messaging with our open-source Android SMS
|
||||
Gateway.
|
||||
</Text>
|
||||
@@ -65,9 +70,7 @@ export default function IntroSection() {
|
||||
colorScheme={'blue'}
|
||||
bg={'blue.400'}
|
||||
_hover={{ bg: 'blue.500' }}
|
||||
onClick={() => {
|
||||
Router.push('/register')
|
||||
}}
|
||||
onClick={handleGetStarted}
|
||||
>
|
||||
Get Started
|
||||
</Button>
|
||||
@@ -95,29 +98,17 @@ export default function IntroSection() {
|
||||
position={'relative'}
|
||||
height={'400px'}
|
||||
rounded={'2xl'}
|
||||
boxShadow={'2xl'}
|
||||
boxShadow={'xs'}
|
||||
width={'full'}
|
||||
overflow={'hidden'}
|
||||
>
|
||||
<IconButton
|
||||
aria-label={'Play Button'}
|
||||
variant={'ghost'}
|
||||
_hover={{ bg: 'transparent' }}
|
||||
icon={<PlayIcon w={12} h={12} />}
|
||||
size={'lg'}
|
||||
color={'white'}
|
||||
position={'absolute'}
|
||||
left={'50%'}
|
||||
top={'50%'}
|
||||
transform={'translateX(-50%) translateY(-50%)'}
|
||||
/>
|
||||
<Image
|
||||
alt={'Hero Image'}
|
||||
fit={'cover'}
|
||||
align={'center'}
|
||||
w={'100%'}
|
||||
h={'100%'}
|
||||
src={'/images/landing-img1.jpeg'}
|
||||
// w={'100%'}
|
||||
// h={'100%'}
|
||||
src={'/images/smsgatewayandroid.png'}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export const howItWorksContent = [
|
||||
{
|
||||
title: 'Step 1: Download The Android App',
|
||||
title: 'Step 1: Download The Android App from textbee.vernu.dev/android',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
|
||||
'',
|
||||
},
|
||||
{
|
||||
title: 'Step 2: Generate an API Key from the dashboard',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
|
||||
'',
|
||||
},
|
||||
{
|
||||
title:
|
||||
'Step 3: Scan the QR/ enter your api key manually and enable the gateway app',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
|
||||
'',
|
||||
},
|
||||
{
|
||||
title: 'Step 4: Start sending',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
|
||||
'You can now send SMS from the dashboard. or visit the API docs at https://api.textbee.vernu.dev to send SMS programatically',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -3,10 +3,10 @@ import Head from 'next/head'
|
||||
export default function Meta() {
|
||||
return (
|
||||
<Head>
|
||||
<title>SMS Gateway</title>
|
||||
<title>TextBee - SMS Gateway</title>
|
||||
<meta name='viewport' content='initial-scale=1.0, width=device-width' />
|
||||
<meta name='description' content='Android SMS Gateway' />
|
||||
<meta name='keywords' content='android, sms, gateway, sms-gateway' />
|
||||
<meta name='keywords' content='android, text, sms, gateway, sms-gateway' />
|
||||
<meta name='author' content='Israel Abebe' />
|
||||
<link rel='icon' href='/favicon.ico' />
|
||||
</Head>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import axios from 'axios'
|
||||
import { LOCAL_STORAGE_KEY } from '../shared/constants'
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
const customAxios = axios.create({
|
||||
baseURL: process.env.NEXT_PUBLIC_API_BASE_URL,
|
||||
})
|
||||
|
||||
axiosInstance.interceptors.request.use((config) => {
|
||||
customAxios.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem(LOCAL_STORAGE_KEY.TOKEN)
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
@@ -13,4 +13,4 @@ axiosInstance.interceptors.request.use((config) => {
|
||||
return config
|
||||
})
|
||||
|
||||
export default axiosInstance
|
||||
export default customAxios
|
||||
@@ -1,6 +1,15 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/android',
|
||||
destination: 'https://appdistribution.firebase.dev/i/1439f7af2d1e8e8e',
|
||||
permanent: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
@@ -2,11 +2,12 @@ import '../styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
import { Provider } from 'react-redux'
|
||||
import { store } from '../store/store'
|
||||
import { ChakraProvider } from '@chakra-ui/react'
|
||||
import { Box, ChakraProvider } from '@chakra-ui/react'
|
||||
import Navbar from '../components/Navbar'
|
||||
import Meta from '../components/meta/Meta'
|
||||
import { GoogleOAuthProvider } from '@react-oauth/google'
|
||||
import ErrorBoundary from '../components/ErrorBoundary'
|
||||
import Footer from '../components/Footer'
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
@@ -20,6 +21,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
<Footer />
|
||||
</ChakraProvider>
|
||||
</GoogleOAuthProvider>
|
||||
</Provider>
|
||||
@@ -28,7 +30,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||
}
|
||||
|
||||
const Wrapper = ({ children }) => {
|
||||
return <>{children}</>
|
||||
return <Box minH='75vh'>{children}</Box>
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
@@ -28,19 +28,18 @@ export default function Dashboard() {
|
||||
<>
|
||||
<UserStats />
|
||||
<Box maxW='7xl' mx={'auto'} pt={5} px={{ base: 2, sm: 12, md: 17 }}>
|
||||
<Flex justifyContent='space-evenly'>
|
||||
<GenerateApiKey />
|
||||
<SendSMS />
|
||||
</Flex>
|
||||
<Flex justifyContent='space-eve nly'></Flex>
|
||||
|
||||
<br />
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={{ base: 5, lg: 8 }}>
|
||||
<Box backdropBlur='2xl' borderWidth='1px' borderRadius='lg'>
|
||||
<Box backdropBlur='2xl' borderWidth='0px' borderRadius='lg'>
|
||||
<GenerateApiKey />
|
||||
<ErrorBoundary>
|
||||
<ApiKeyList />
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
<Box backdropBlur='2xl' borderWidth='1px' borderRadius='lg'>
|
||||
<Box backdropBlur='2xl' borderWidth='0px' borderRadius='lg'>
|
||||
<SendSMS />
|
||||
<ErrorBoundary>
|
||||
<DeviceList />
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
import { Box, Container } from '@chakra-ui/react'
|
||||
import { Container } from '@chakra-ui/react'
|
||||
import { useGoogleOneTapLogin } from '@react-oauth/google'
|
||||
import Image from 'next/image'
|
||||
import Router from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import FeaturesSection from '../components/home/FeaturesSection'
|
||||
import HowItWorksSection from '../components/home/HowItWorksSection'
|
||||
import IntroSection from '../components/home/IntroSection'
|
||||
import { loginWithGoogle, selectAuth } from '../store/authReducer'
|
||||
import wageSvg from '../public/images/wave.svg'
|
||||
|
||||
const Wave = ({ rotate }: { rotate?: boolean }) => (
|
||||
<Box transform={rotate ? 'rotate(180deg)' : ''}>
|
||||
<Image src={wageSvg} alt={'wave'} />
|
||||
</Box>
|
||||
)
|
||||
import DownloadAppSection from '../components/home/DownloadAppSection'
|
||||
import CodeSnippetSection from '../components/home/CodeSnippetSection'
|
||||
|
||||
export default function HomePage() {
|
||||
const { accessToken, user } = useSelector(selectAuth)
|
||||
useEffect(() => {
|
||||
if (accessToken && user) {
|
||||
Router.push('/dashboard')
|
||||
}
|
||||
}, [accessToken, user])
|
||||
const { user } = useSelector(selectAuth)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
@@ -41,11 +30,10 @@ export default function HomePage() {
|
||||
return (
|
||||
<Container maxW={'7xl'}>
|
||||
<IntroSection />
|
||||
<Wave rotate />
|
||||
<FeaturesSection />
|
||||
<Wave />
|
||||
<HowItWorksSection />
|
||||
<Wave />
|
||||
<DownloadAppSection />
|
||||
<CodeSnippetSection />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
web/public/images/smsgatewayandroid.png
Normal file
BIN
web/public/images/smsgatewayandroid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 242 KiB |
@@ -1,4 +1,4 @@
|
||||
import axiosInstance from '../lib/axiosInstance'
|
||||
import axios from '../lib/customAxios'
|
||||
import {
|
||||
GoogleLoginRequestPayload,
|
||||
LoginRequestPayload,
|
||||
@@ -11,41 +11,46 @@ import {
|
||||
export const loginRequest = async (
|
||||
payload: LoginRequestPayload
|
||||
): Promise<LoginResponse> => {
|
||||
const res = await axiosInstance.post(`/auth/login`, payload)
|
||||
const res = await axios.post(`/auth/login`, payload)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const loginWithGoogleRequest = async (
|
||||
payload: GoogleLoginRequestPayload
|
||||
): Promise<LoginResponse> => {
|
||||
const res = await axiosInstance.post(`/auth/google-login`, payload)
|
||||
const res = await axios.post(`/auth/google-login`, payload)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const registerRequest = async (
|
||||
payload: RegisterRequestPayload
|
||||
): Promise<RegisterResponse> => {
|
||||
const res = await axiosInstance.post(`/auth/register`, payload)
|
||||
const res = await axios.post(`/auth/register`, payload)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const getCurrentUserRequest = async () => {
|
||||
const res = await axios.get(`/auth/who-am-i`)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const generateApiKeyRequest = async () => {
|
||||
const res = await axiosInstance.post(`/auth/api-keys`, {})
|
||||
const res = await axios.post(`/auth/api-keys`, {})
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const getApiKeyListRequest = async () => {
|
||||
const res = await axiosInstance.get(`/auth/api-keys`)
|
||||
const res = await axios.get(`/auth/api-keys`)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const deleteApiKeyRequest = async (id: string) => {
|
||||
const res = await axiosInstance.delete(`/auth/api-keys/${id}`)
|
||||
const res = await axios.delete(`/auth/api-keys/${id}`)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export const getDeviceListRequest = async () => {
|
||||
const res = await axiosInstance.get(`/gateway/devices`)
|
||||
const res = await axios.get(`/gateway/devices`)
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
@@ -53,7 +58,7 @@ export const sendSMSRequest = async (
|
||||
deviceId: string,
|
||||
payload: SendSMSRequestPayload
|
||||
) => {
|
||||
const res = await axiosInstance.post(
|
||||
const res = await axios.post(
|
||||
`/gateway/devices/${deviceId}/sendSMS`,
|
||||
payload
|
||||
)
|
||||
|
||||
@@ -43,6 +43,10 @@ export interface LoginResponse extends BaseResponse {
|
||||
|
||||
export type RegisterResponse = LoginResponse
|
||||
|
||||
export interface CurrentUserResponse extends BaseResponse {
|
||||
data: UserEntity
|
||||
}
|
||||
|
||||
export interface SendSMSRequestPayload {
|
||||
receivers: string[]
|
||||
smsBody: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const KEY_PREFIX = 'sms0'
|
||||
export const KEY_PREFIX = 'textbee'
|
||||
|
||||
export const LOCAL_STORAGE_KEY = {
|
||||
USER: `${KEY_PREFIX}.user`,
|
||||
|
||||
Reference in New Issue
Block a user