Skip to content

Commit

Permalink
Enable Account without Organization (#836)
Browse files Browse the repository at this point in the history
* Permit account navigation without organization

* Refactor ProtectedRoutes.tsx

Now protected routes is a function that return a RouteObject

* Fix create organization route layout

* Fix prettier

* Fix comments

* Use ClientsGrid

* Move createOrganization.tsx to Dashboard elements

* Fix redirect after org creation

* Propagate explicitly the outlet context

* Refactor verify components

Moved Verify page to elements and use better name to verification pending form

* Implement auth layout organization create

It also implements skip button for organization form

* Move create orga element to correct folder

* Implement dashboard create organization

* Fix lint and prettier

* Refactor directories

* Run prettier
  • Loading branch information
selankon authored Nov 22, 2024
1 parent 9e2899c commit c252b71
Show file tree
Hide file tree
Showing 24 changed files with 495 additions and 404 deletions.
3 changes: 2 additions & 1 deletion src/components/Account/Teams.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Avatar, Badge, Box, HStack, VStack } from '@chakra-ui/react'
import { OrganizationName } from '@vocdoni/chakra-components'
import { OrganizationProvider } from '@vocdoni/react-providers'
import { NoOrganizations } from '~components/Organization/NoOrganizations'
import { UserRole } from '~src/queries/account'

const Teams = ({ roles }: { roles: UserRole[] }) => {
if (!roles) return null
if (!roles || !roles.length) return <NoOrganizations />

return (
<VStack spacing={4} align='stretch'>
Expand Down
7 changes: 3 additions & 4 deletions src/components/Auth/SignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { NavLink, useNavigate } from 'react-router-dom'
import { api, ApiEndpoints, UnverifiedApiError } from '~components/Auth/api'
import { ILoginParams } from '~components/Auth/authQueries'
import { useAuth } from '~components/Auth/useAuth'
import { VerifyAccountNeeded } from '~components/Auth/Verify'
import { VerificationPending } from '~components/Auth/Verify'
import FormSubmitMessage from '~components/Layout/FormSubmitMessage'
import InputPassword from '~components/Layout/InputPassword'
import { Routes } from '~src/router/routes'
Expand All @@ -22,10 +22,9 @@ type FormData = {
const useVerificationCodeStatus = () =>
useMutation({
mutationFn: async (email: string) => {
const response = await api<{ email: string; expiration: string; valid: boolean }>(
return await api<{ email: string; expiration: string; valid: boolean }>(
`${ApiEndpoints.VerifyCode}?email=${encodeURIComponent(email)}`
)
return response
},
})

Expand Down Expand Up @@ -92,7 +91,7 @@ const SignIn = ({ email: emailProp }: { email?: string }) => {
}

if (verifyNeeded) {
return <VerifyAccountNeeded email={email} />
return <VerificationPending email={email} />
}

return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/Auth/SignUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Trans, useTranslation } from 'react-i18next'
import { Navigate, NavLink, Link as ReactRouterLink } from 'react-router-dom'
import { IRegisterParams } from '~components/Auth/authQueries'
import { useAuth } from '~components/Auth/useAuth'
import { VerifyAccountNeeded } from '~components/Auth/Verify'
import { VerificationPending } from '~components/Auth/Verify'
import FormSubmitMessage from '~components/Layout/FormSubmitMessage'
import InputPassword from '~components/Layout/InputPassword'
import { useSignupFromInvite } from '~src/queries/account'
Expand Down Expand Up @@ -59,7 +59,7 @@ const SignUp = ({ invite }: SignupProps) => {

// normally registered accounts need verification
if (register.isSuccess) {
return <VerifyAccountNeeded email={email} />
return <VerificationPending email={email} />
}

// accounts coming from invites don't need verification
Expand Down
70 changes: 5 additions & 65 deletions src/components/Auth/Verify.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,14 @@
import { Box, Button, Divider, Flex, FormControl, FormErrorMessage, Heading, Input, Text } from '@chakra-ui/react'
import { Button, Divider, Flex, Input, Text } from '@chakra-ui/react'
import { useCallback, useEffect, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { Link as ReactRouterLink, useNavigate, useOutletContext, useSearchParams } from 'react-router-dom'
import { Link as ReactRouterLink, useNavigate, useOutletContext } from 'react-router-dom'
import { useResendVerificationMail } from '~components/Auth/authQueries'
import { useAuth } from '~components/Auth/useAuth'
import FormSubmitMessage from '~components/Layout/FormSubmitMessage'
import { AuthOutletContextType } from '~elements/LayoutAuth'
import { Routes } from '~src/router/routes'
import { Loading } from '~src/router/SuspenseLoader'

const Verify = () => {
const navigate = useNavigate()
const { t } = useTranslation()
const [searchParams] = useSearchParams()
const {
mailVerify: { isIdle, isPending, isError: isMutationError, error, mutateAsync },
} = useAuth()

const email = searchParams.get('email')
const code = searchParams.get('code')
const isLoading = isIdle || isPending
const isError = !email || isMutationError || (import.meta.env.VOCDONI_ENVIRONMENT !== 'dev' && !code)

// Trigger email verification on component load
useEffect(() => {
mutateAsync({ email, code }).then(() => navigate(Routes.dashboard.base))
}, [])

let title = t('verify_mail.verifying_title', { email: email, defaultValue: 'Verifying {{ email }}' })
let subTitle = t('verify_mail.verifying_subtitle', {
defaultValue: 'Await until we verify your email address. You will be redirect on success.',
})
// dev enviorment permits empty code
if (isError) {
title = t('verify_mail.error_title', { email: email, defaultValue: 'Error verifying {{ email }}' })
subTitle = t('verify_mail.error_subtitle', {
defaultValue:
'We found an error verifying your email, please check verification mail to ensure all data is correct',
})
}

return (
<Flex direction='column'>
<Box me='auto'>
<Heading fontSize='36px' mb='10px'>
{title}
</Heading>
<Text mb='36px' ms='4px' color={'verify_subtitle'} fontWeight='400' fontSize='md'>
{subTitle}
</Text>
</Box>
{isLoading && !isError && (
<Box height={'100px'}>
<Loading minHeight={1} />
</Box>
)}
<Box>
<FormControl isInvalid={isError}>
{isError && (
<FormErrorMessage>
{error?.message || t('error.error_doing_things', { defaultValue: 'Error al realizar la operaciΓ³n' })}
</FormErrorMessage>
)}
</FormControl>
</Box>
</Flex>
)
}
export const verificationSuccessRedirect = Routes.auth.organizationCreate

interface IVerifyAccountProps {
email: string
Expand All @@ -81,7 +23,7 @@ const VerifyForm = ({ email }: IVerifyAccountProps) => {
} = useAuth()

const verify = useCallback(() => {
verifyAsync({ email, code }).then(() => navigate(Routes.dashboard.base))
verifyAsync({ email, code }).then(() => navigate(verificationSuccessRedirect))
}, [code, email])

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -105,7 +47,7 @@ const VerifyForm = ({ email }: IVerifyAccountProps) => {
)
}

export const VerifyAccountNeeded = ({ email }: IVerifyAccountProps) => {
export const VerificationPending = ({ email }: IVerifyAccountProps) => {
const { t } = useTranslation()
const { setTitle, setSubTitle } = useOutletContext<AuthOutletContextType>()
const {
Expand Down Expand Up @@ -168,5 +110,3 @@ export const VerifyAccountNeeded = ({ email }: IVerifyAccountProps) => {
</>
)
}

export default Verify
201 changes: 103 additions & 98 deletions src/components/Home/Clients.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Card, CardBody, CardHeader, Grid, Image, Text } from '@chakra-ui/react'
import { Card, CardBody, CardHeader, Grid, GridProps, Image, Text } from '@chakra-ui/react'
import { useTranslation } from 'react-i18next'
import barca from '/assets/barca.png'
import bellpuig from '/assets/bellpuig.svg.png'
Expand Down Expand Up @@ -26,104 +26,109 @@ const Clients = () => {
>
{t('home.clients_title')}
</Text>
<Grid
as='section'
width='full'
m='0 auto'
px={{
base: '10px',
sm: '20px',
md: '80px',
}}
maxW={{ base: '100%', sm: '70%', sm2: '80%', lg: '900px' }}
flexDirection={{ base: 'column', sm: 'row' }}
justifyContent='center'
mb={{ base: '60px' }}
gridTemplateColumns='repeat(5, 1fr)'
gridRowGap='50px'
>
<Card variant='client'>
<CardHeader>
<Image src={barca} h={{ base: '32.5px', sm2: '65px', lg: '70px' }} />
</CardHeader>
<CardBody>
<Text as='span'>F.C. Barcelona</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={omnium} h={{ base: '27.5px', sm2: '75px', lg: '87px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Omnium Cultural</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={berga} h={{ base: '35px', sm2: '70px', lg: '81px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Ajuntament Berga</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={bisbal} h={{ base: '36px', sm2: '72px', lg: '83px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Ajuntament la Bisbal</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={coec} h={{ base: '17.5px', sm2: '35px', lg: '45px' }} />
</CardHeader>
<CardBody>
<Text as='span'>COEC</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={erc} h={{ base: '19px', sm2: '38px', lg: '52px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Esquerra Republicana</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={bellpuig} h={{ base: '36px', sm2: '72px', lg: '83px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Ajuntament Bellpuig</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={ticanoia} h={{ base: '13px', sm2: '26px', lg: '32px' }} />
</CardHeader>
<CardBody>
<Text as='span'>TIC Anoia</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={decidim} h={{ base: '31px', sm2: '62px', lg: '70px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Decidim</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={bloock} h={{ base: '17.5px', sm2: '35px', lg: '33px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Bloock</Text>
</CardBody>
</Card>
</Grid>
</>
)
}

export const ClientsGrid = (props: GridProps) => (
<Grid
as='section'
width='full'
m='0 auto'
px={{
base: '10px',
sm: '20px',
md: '80px',
}}
maxW={{ base: '100%', sm: '70%', sm2: '80%', lg: '900px' }}
flexDirection={{ base: 'column', sm: 'row' }}
justifyContent='center'
mb={{ base: '60px' }}
gridTemplateColumns='repeat(5, 1fr)'
gridRowGap='50px'
{...props}
>
<Card variant='client'>
<CardHeader>
<Image src={barca} h={{ base: '32.5px', sm2: '65px', lg: '70px' }} />
</CardHeader>
<CardBody>
<Text as='span'>F.C. Barcelona</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={omnium} h={{ base: '27.5px', sm2: '75px', lg: '87px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Omnium Cultural</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={berga} h={{ base: '35px', sm2: '70px', lg: '81px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Ajuntament Berga</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={bisbal} h={{ base: '36px', sm2: '72px', lg: '83px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Ajuntament la Bisbal</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={coec} h={{ base: '17.5px', sm2: '35px', lg: '45px' }} />
</CardHeader>
<CardBody>
<Text as='span'>COEC</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={erc} h={{ base: '19px', sm2: '38px', lg: '52px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Esquerra Republicana</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={bellpuig} h={{ base: '36px', sm2: '72px', lg: '83px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Ajuntament Bellpuig</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={ticanoia} h={{ base: '13px', sm2: '26px', lg: '32px' }} />
</CardHeader>
<CardBody>
<Text as='span'>TIC Anoia</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={decidim} h={{ base: '31px', sm2: '62px', lg: '70px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Decidim</Text>
</CardBody>
</Card>
<Card variant='client'>
<CardHeader>
<Image src={bloock} h={{ base: '17.5px', sm2: '35px', lg: '33px' }} />
</CardHeader>
<CardBody>
<Text as='span'>Bloock</Text>
</CardBody>
</Card>
</Grid>
)

export default Clients
Loading

2 comments on commit c252b71

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.