feat(pages): add /application/guilds/join (#2)
				
					
				
			This commit is contained in:
		@@ -240,6 +240,7 @@ export const Application: React.FC<ApplicationProps> = (props) => {
 | 
				
			|||||||
        </Sidebar>
 | 
					        </Sidebar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
 | 
					          id='application-page-content'
 | 
				
			||||||
          className={classNames(
 | 
					          className={classNames(
 | 
				
			||||||
            'top-0 h-full-without-header flex flex-col flex-1 z-0 overflow-y-auto transition',
 | 
					            'top-0 h-full-without-header flex flex-col flex-1 z-0 overflow-y-auto transition',
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ export const CreateGuild: React.FC = () => {
 | 
				
			|||||||
  const { t } = useTranslation()
 | 
					  const { t } = useTranslation()
 | 
				
			||||||
  const router = useRouter()
 | 
					  const router = useRouter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { formState, message, errors, getErrorTranslation, handleSubmit } =
 | 
					  const { fetchState, message, errors, getErrorTranslation, handleSubmit } =
 | 
				
			||||||
    useForm({
 | 
					    useForm({
 | 
				
			||||||
      validateSchemaObject: {
 | 
					      validateSchemaObject: {
 | 
				
			||||||
        name: guildSchema.name,
 | 
					        name: guildSchema.name,
 | 
				
			||||||
@@ -76,7 +76,7 @@ export const CreateGuild: React.FC = () => {
 | 
				
			|||||||
          {t('application:create')}
 | 
					          {t('application:create')}
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      </Form>
 | 
					      </Form>
 | 
				
			||||||
      <FormState id='message' state={formState} message={message} />
 | 
					      <FormState id='message' state={fetchState} message={message} />
 | 
				
			||||||
    </Main>
 | 
					    </Main>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { Meta, Story } from '@storybook/react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Guild as Component, GuildProps } from './Guild'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Stories: Meta = {
 | 
				
			||||||
 | 
					  title: 'Guild',
 | 
				
			||||||
 | 
					  component: Component
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Stories
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Guild: Story<GuildProps> = (arguments_) => {
 | 
				
			||||||
 | 
					  return <Component {...arguments_} />
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Guild.args = {
 | 
				
			||||||
 | 
					  guild: {
 | 
				
			||||||
 | 
					    id: 1,
 | 
				
			||||||
 | 
					    name: 'GuildExample',
 | 
				
			||||||
 | 
					    description: 'guild example.',
 | 
				
			||||||
 | 
					    icon: null,
 | 
				
			||||||
 | 
					    createdAt: new Date().toISOString(),
 | 
				
			||||||
 | 
					    updatedAt: new Date().toISOString(),
 | 
				
			||||||
 | 
					    membersCount: 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								components/Application/JoinGuildsPublic/Guild/Guild.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								components/Application/JoinGuildsPublic/Guild/Guild.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					import { render } from '@testing-library/react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Guild } from './Guild'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<Guild />', () => {
 | 
				
			||||||
 | 
					  it('should render successfully', () => {
 | 
				
			||||||
 | 
					    const { baseElement } = render(
 | 
				
			||||||
 | 
					      <Guild
 | 
				
			||||||
 | 
					        guild={{
 | 
				
			||||||
 | 
					          id: 1,
 | 
				
			||||||
 | 
					          name: 'GuildExample',
 | 
				
			||||||
 | 
					          description: 'guild example.',
 | 
				
			||||||
 | 
					          icon: null,
 | 
				
			||||||
 | 
					          createdAt: new Date().toISOString(),
 | 
				
			||||||
 | 
					          updatedAt: new Date().toISOString(),
 | 
				
			||||||
 | 
					          membersCount: 1
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    expect(baseElement).toBeTruthy()
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										35
									
								
								components/Application/JoinGuildsPublic/Guild/Guild.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								components/Application/JoinGuildsPublic/Guild/Guild.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import Image from 'next/image'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { GuildPublic } from 'models/Guild'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface GuildProps {
 | 
				
			||||||
 | 
					  guild: GuildPublic
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Guild: React.FC<GuildProps> = (props) => {
 | 
				
			||||||
 | 
					  const { guild } = props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      key={guild.id}
 | 
				
			||||||
 | 
					      className='max-w-sm flex flex-col items-center justify-center border-gray-500 dark:border-gray-700 p-4 cursor-pointer rounded shadow-lg border transition duration-200 ease-in-out hover:-translate-y-2 hover:shadow-none'
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Image
 | 
				
			||||||
 | 
					        className='rounded-full'
 | 
				
			||||||
 | 
					        src={guild.icon != null ? guild.icon : '/images/data/guild-default.png'}
 | 
				
			||||||
 | 
					        alt='logo'
 | 
				
			||||||
 | 
					        width={80}
 | 
				
			||||||
 | 
					        height={80}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <div className='m-2 text-center mt-3'>
 | 
				
			||||||
 | 
					        <h3 data-cy='guild-name' className='font-bold text-xl mb-2'>
 | 
				
			||||||
 | 
					          {guild.name}
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					        <p className='text-base w-11/12 mx-auto'>{guild.description}</p>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <p className='flex flex-col text-green-800 dark:text-green-400 mt-4'>
 | 
				
			||||||
 | 
					        {guild.membersCount} members
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								components/Application/JoinGuildsPublic/Guild/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								components/Application/JoinGuildsPublic/Guild/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from './Guild'
 | 
				
			||||||
							
								
								
									
										75
									
								
								components/Application/JoinGuildsPublic/JoinGuildsPublic.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								components/Application/JoinGuildsPublic/JoinGuildsPublic.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					import { useCallback, useEffect, useState, useRef } from 'react'
 | 
				
			||||||
 | 
					import InfiniteScroll from 'react-infinite-scroll-component'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useAuthentication } from 'utils/authentication'
 | 
				
			||||||
 | 
					import { GuildPublic } from 'models/Guild'
 | 
				
			||||||
 | 
					import { Loader } from 'components/design/Loader'
 | 
				
			||||||
 | 
					import { useFetchState } from 'hooks/useFetchState'
 | 
				
			||||||
 | 
					import { Guild } from './Guild'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const JoinGuildsPublic: React.FC = () => {
 | 
				
			||||||
 | 
					  const [guilds, setGuilds] = useState<GuildPublic[]>([])
 | 
				
			||||||
 | 
					  const [hasMore, setHasMore] = useState(true)
 | 
				
			||||||
 | 
					  const [inputSearch, setInputSearch] = useState('')
 | 
				
			||||||
 | 
					  const [fetchState, setFetchState] = useFetchState('idle')
 | 
				
			||||||
 | 
					  const afterId = useRef<number | null>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { authentication } = useAuthentication()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const fetchGuilds = useCallback(async (): Promise<void> => {
 | 
				
			||||||
 | 
					    if (fetchState !== 'idle') {
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setFetchState('loading')
 | 
				
			||||||
 | 
					    const { data } = await authentication.api.get<GuildPublic[]>(
 | 
				
			||||||
 | 
					      `/guilds/public?limit=20&search=${inputSearch}${
 | 
				
			||||||
 | 
					        afterId.current != null ? `&after=${afterId.current}` : ''
 | 
				
			||||||
 | 
					      }`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    afterId.current = data.length > 0 ? data[data.length - 1].id : null
 | 
				
			||||||
 | 
					    setGuilds((oldGuilds) => {
 | 
				
			||||||
 | 
					      return [...oldGuilds, ...data]
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    setHasMore(data.length > 0)
 | 
				
			||||||
 | 
					    setFetchState('idle')
 | 
				
			||||||
 | 
					  }, [authentication, fetchState, setFetchState, inputSearch])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    afterId.current = null
 | 
				
			||||||
 | 
					    setGuilds([])
 | 
				
			||||||
 | 
					    fetchGuilds().catch((error) => {
 | 
				
			||||||
 | 
					      console.error(error)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }, [inputSearch]) // eslint-disable-line react-hooks/exhaustive-deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
 | 
				
			||||||
 | 
					    setInputSearch(event.target.value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <input
 | 
				
			||||||
 | 
					        data-cy='search-guild-input'
 | 
				
			||||||
 | 
					        onChange={handleChange}
 | 
				
			||||||
 | 
					        className='w-10/12 sm:w-8/12 md:w-6/12 lg:w-5/12 bg-white dark:bg-[#3B3B3B] border-gray-500 dark:border-gray-700 p-3 my-6 mt-16 mx-auto rounded-md border'
 | 
				
			||||||
 | 
					        type='search'
 | 
				
			||||||
 | 
					        name='search-guild'
 | 
				
			||||||
 | 
					        placeholder='🔎  Search...'
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <div className='w-full flex items-center justify-center p-12'>
 | 
				
			||||||
 | 
					        <InfiniteScroll
 | 
				
			||||||
 | 
					          className='guilds-list max-w-[1600px] grid grid-cols-1 xl:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-8 !overflow-hidden'
 | 
				
			||||||
 | 
					          dataLength={guilds.length}
 | 
				
			||||||
 | 
					          next={fetchGuilds}
 | 
				
			||||||
 | 
					          scrollableTarget='application-page-content'
 | 
				
			||||||
 | 
					          hasMore={hasMore}
 | 
				
			||||||
 | 
					          loader={<Loader />}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          {guilds.map((guild) => {
 | 
				
			||||||
 | 
					            return <Guild guild={guild} key={guild.id} />
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        </InfiniteScroll>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								components/Application/JoinGuildsPublic/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								components/Application/JoinGuildsPublic/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from './JoinGuildsPublic'
 | 
				
			||||||
@@ -29,7 +29,7 @@ export const Authentication: React.FC<AuthenticationProps> = (props) => {
 | 
				
			|||||||
  const { lang, t } = useTranslation()
 | 
					  const { lang, t } = useTranslation()
 | 
				
			||||||
  const { theme } = useTheme()
 | 
					  const { theme } = useTheme()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { errors, formState, message, getErrorTranslation, handleSubmit } =
 | 
					  const { errors, fetchState, message, getErrorTranslation, handleSubmit } =
 | 
				
			||||||
    useForm({
 | 
					    useForm({
 | 
				
			||||||
      validateSchemaObject: {
 | 
					      validateSchemaObject: {
 | 
				
			||||||
        ...(mode === 'signup' && { name: userSchema.name }),
 | 
					        ...(mode === 'signup' && { name: userSchema.name }),
 | 
				
			||||||
@@ -139,7 +139,7 @@ export const Authentication: React.FC<AuthenticationProps> = (props) => {
 | 
				
			|||||||
          </Link>
 | 
					          </Link>
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
      </AuthenticationForm>
 | 
					      </AuthenticationForm>
 | 
				
			||||||
      <FormState id='message' state={formState} message={message} />
 | 
					      <FormState id='message' state={fetchState} message={message} />
 | 
				
			||||||
    </Main>
 | 
					    </Main>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import classNames from 'classnames'
 | 
					import classNames from 'classnames'
 | 
				
			||||||
import useTranslation from 'next-translate/useTranslation'
 | 
					import useTranslation from 'next-translate/useTranslation'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormState as FormStateType } from 'hooks/useFormState'
 | 
					import { FetchState as FormStateType } from 'hooks/useFetchState'
 | 
				
			||||||
import { Loader } from '../Loader'
 | 
					import { Loader } from '../Loader'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface FormStateProps {
 | 
					export interface FormStateProps {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,3 +6,8 @@ export const guild = {
 | 
				
			|||||||
  createdAt: new Date().toISOString(),
 | 
					  createdAt: new Date().toISOString(),
 | 
				
			||||||
  updatedAt: new Date().toISOString()
 | 
					  updatedAt: new Date().toISOString()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const guild2 = {
 | 
				
			||||||
 | 
					  ...guild,
 | 
				
			||||||
 | 
					  name: 'app'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								cypress/fixtures/guilds/public/get.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								cypress/fixtures/guilds/public/get.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					import { Handler } from '../../handler'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { guild, guild2 } from '../guild'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getGuildsPublicEmptyHandler: Handler = {
 | 
				
			||||||
 | 
					  method: 'GET',
 | 
				
			||||||
 | 
					  url: '/guilds/public',
 | 
				
			||||||
 | 
					  response: {
 | 
				
			||||||
 | 
					    statusCode: 200,
 | 
				
			||||||
 | 
					    body: []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getGuildsPublicHandler: Handler = {
 | 
				
			||||||
 | 
					  method: 'GET',
 | 
				
			||||||
 | 
					  url: '/guilds/public',
 | 
				
			||||||
 | 
					  response: {
 | 
				
			||||||
 | 
					    statusCode: 200,
 | 
				
			||||||
 | 
					    body: [
 | 
				
			||||||
 | 
					      { ...guild, membersCount: 1 },
 | 
				
			||||||
 | 
					      { ...guild2, membersCount: 1 }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getGuildsPublicSearchHandler: Handler = {
 | 
				
			||||||
 | 
					  method: 'GET',
 | 
				
			||||||
 | 
					  url: '/guilds/public',
 | 
				
			||||||
 | 
					  response: {
 | 
				
			||||||
 | 
					    statusCode: 200,
 | 
				
			||||||
 | 
					    body: [{ ...guild2, membersCount: 1 }]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										55
									
								
								cypress/integration/pages/application/guilds/join.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								cypress/integration/pages/application/guilds/join.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					  getGuildsPublicEmptyHandler,
 | 
				
			||||||
 | 
					  getGuildsPublicHandler,
 | 
				
			||||||
 | 
					  getGuildsPublicSearchHandler
 | 
				
			||||||
 | 
					} from '../../../../fixtures/guilds/public/get'
 | 
				
			||||||
 | 
					import { authenticationHandlers } from '../../../../fixtures/handler'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Pages > /application/guilds/join', () => {
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    cy.task('stopMockServer')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should shows no guild if there are no public guilds', () => {
 | 
				
			||||||
 | 
					    cy.task('startMockServer', [
 | 
				
			||||||
 | 
					      ...authenticationHandlers,
 | 
				
			||||||
 | 
					      getGuildsPublicEmptyHandler
 | 
				
			||||||
 | 
					    ]).setCookie('refreshToken', 'refresh-token')
 | 
				
			||||||
 | 
					    cy.visit('/application/guilds/join')
 | 
				
			||||||
 | 
					    cy.get('.guilds-list').children().should('have.length', 0)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should shows loader with internal api server error', () => {
 | 
				
			||||||
 | 
					    cy.task('startMockServer', [...authenticationHandlers]).setCookie(
 | 
				
			||||||
 | 
					      'refreshToken',
 | 
				
			||||||
 | 
					      'refresh-token'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    cy.visit('/application/guilds/join')
 | 
				
			||||||
 | 
					    cy.get('.guilds-list').children().should('have.length', 1)
 | 
				
			||||||
 | 
					    cy.get('[data-testid=progress-spinner]').should('be.visible')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should shows all the guilds', () => {
 | 
				
			||||||
 | 
					    cy.task('startMockServer', [
 | 
				
			||||||
 | 
					      ...authenticationHandlers,
 | 
				
			||||||
 | 
					      getGuildsPublicHandler
 | 
				
			||||||
 | 
					    ]).setCookie('refreshToken', 'refresh-token')
 | 
				
			||||||
 | 
					    cy.visit('/application/guilds/join')
 | 
				
			||||||
 | 
					    cy.get('.guilds-list').children().should('have.length', 2)
 | 
				
			||||||
 | 
					    cy.get('.guilds-list [data-cy=guild-name]:first').should(
 | 
				
			||||||
 | 
					      'have.text',
 | 
				
			||||||
 | 
					      'GuildExample'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should shows the searched guild', () => {
 | 
				
			||||||
 | 
					    cy.task('startMockServer', [
 | 
				
			||||||
 | 
					      ...authenticationHandlers,
 | 
				
			||||||
 | 
					      getGuildsPublicSearchHandler
 | 
				
			||||||
 | 
					    ]).setCookie('refreshToken', 'refresh-token')
 | 
				
			||||||
 | 
					    cy.visit('/application/guilds/join')
 | 
				
			||||||
 | 
					    cy.get('[data-cy=search-guild-input]').type('app')
 | 
				
			||||||
 | 
					    cy.get('.guilds-list').children().should('have.length', 1)
 | 
				
			||||||
 | 
					    cy.get('.guilds-list [data-cy=guild-name]:first').should('have.text', 'app')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										15
									
								
								hooks/useFetchState.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								hooks/useFetchState.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const fetchState = ['idle', 'loading', 'error', 'success'] as const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type FetchState = typeof fetchState[number]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useFetchState = (
 | 
				
			||||||
 | 
					  initialFetchState: FetchState = 'idle'
 | 
				
			||||||
 | 
					): [
 | 
				
			||||||
 | 
					  fetchState: FetchState,
 | 
				
			||||||
 | 
					  setFetchState: React.Dispatch<React.SetStateAction<FetchState>>
 | 
				
			||||||
 | 
					] => {
 | 
				
			||||||
 | 
					  const [fetchState, setFetchState] = useState<FetchState>(initialFetchState)
 | 
				
			||||||
 | 
					  return [fetchState, setFetchState]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,7 +4,7 @@ import { Type } from '@sinclair/typebox'
 | 
				
			|||||||
import type { FormDataObject, HandleForm } from 'react-component-form'
 | 
					import type { FormDataObject, HandleForm } from 'react-component-form'
 | 
				
			||||||
import type { ErrorObject } from 'ajv'
 | 
					import type { ErrorObject } from 'ajv'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormState, useFormState } from '../useFormState'
 | 
					import { FetchState, useFetchState } from '../useFetchState'
 | 
				
			||||||
import { ajv } from '../../utils/ajv'
 | 
					import { ajv } from '../../utils/ajv'
 | 
				
			||||||
import { getErrorTranslationKey } from './getErrorTranslationKey'
 | 
					import { getErrorTranslationKey } from './getErrorTranslationKey'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,7 +38,7 @@ export type HandleSubmitCallback = (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface UseFormResult {
 | 
					export interface UseFormResult {
 | 
				
			||||||
  message: string | null
 | 
					  message: string | null
 | 
				
			||||||
  formState: FormState
 | 
					  fetchState: FetchState
 | 
				
			||||||
  getErrorTranslation: GetErrorTranslation
 | 
					  getErrorTranslation: GetErrorTranslation
 | 
				
			||||||
  handleSubmit: HandleSubmit
 | 
					  handleSubmit: HandleSubmit
 | 
				
			||||||
  errors: Errors
 | 
					  errors: Errors
 | 
				
			||||||
@@ -47,7 +47,7 @@ export interface UseFormResult {
 | 
				
			|||||||
export const useForm = (options: UseFormOptions): UseFormResult => {
 | 
					export const useForm = (options: UseFormOptions): UseFormResult => {
 | 
				
			||||||
  const { validateSchemaObject } = options
 | 
					  const { validateSchemaObject } = options
 | 
				
			||||||
  const { t } = useTranslation()
 | 
					  const { t } = useTranslation()
 | 
				
			||||||
  const [formState, setFormState] = useFormState()
 | 
					  const [fetchState, setFetchState] = useFetchState()
 | 
				
			||||||
  const [messageTranslationKey, setMessageTranslationKey] = useState<
 | 
					  const [messageTranslationKey, setMessageTranslationKey] = useState<
 | 
				
			||||||
    string | undefined
 | 
					    string | undefined
 | 
				
			||||||
  >(undefined)
 | 
					  >(undefined)
 | 
				
			||||||
@@ -75,7 +75,7 @@ export const useForm = (options: UseFormOptions): UseFormResult => {
 | 
				
			|||||||
    return async (formData, formElement) => {
 | 
					    return async (formData, formElement) => {
 | 
				
			||||||
      const isValid = validate(formData)
 | 
					      const isValid = validate(formData)
 | 
				
			||||||
      if (!isValid) {
 | 
					      if (!isValid) {
 | 
				
			||||||
        setFormState('error')
 | 
					        setFetchState('error')
 | 
				
			||||||
        const errors: Errors = {}
 | 
					        const errors: Errors = {}
 | 
				
			||||||
        for (const property in validateSchema.properties) {
 | 
					        for (const property in validateSchema.properties) {
 | 
				
			||||||
          errors[property] = validate.errors?.find(findError(`/${property}`))
 | 
					          errors[property] = validate.errors?.find(findError(`/${property}`))
 | 
				
			||||||
@@ -83,15 +83,15 @@ export const useForm = (options: UseFormOptions): UseFormResult => {
 | 
				
			|||||||
        setErrors(errors)
 | 
					        setErrors(errors)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        setErrors({})
 | 
					        setErrors({})
 | 
				
			||||||
        setFormState('loading')
 | 
					        setFetchState('loading')
 | 
				
			||||||
        const message = await callback(formData, formElement)
 | 
					        const message = await callback(formData, formElement)
 | 
				
			||||||
        if (message != null) {
 | 
					        if (message != null) {
 | 
				
			||||||
          setMessageTranslationKey(message.value)
 | 
					          setMessageTranslationKey(message.value)
 | 
				
			||||||
          if (message.type === 'success') {
 | 
					          if (message.type === 'success') {
 | 
				
			||||||
            setFormState('success')
 | 
					            setFetchState('success')
 | 
				
			||||||
            formElement.reset()
 | 
					            formElement.reset()
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            setFormState('error')
 | 
					            setFetchState('error')
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -101,7 +101,7 @@ export const useForm = (options: UseFormOptions): UseFormResult => {
 | 
				
			|||||||
  return {
 | 
					  return {
 | 
				
			||||||
    getErrorTranslation,
 | 
					    getErrorTranslation,
 | 
				
			||||||
    errors,
 | 
					    errors,
 | 
				
			||||||
    formState,
 | 
					    fetchState,
 | 
				
			||||||
    handleSubmit,
 | 
					    handleSubmit,
 | 
				
			||||||
    message: messageTranslationKey != null ? t(messageTranslationKey) : null
 | 
					    message: messageTranslationKey != null ? t(messageTranslationKey) : null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
import { useState } from 'react'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const formState = ['idle', 'loading', 'error', 'success'] as const
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type FormState = typeof formState[number]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const useFormState = (
 | 
					 | 
				
			||||||
  initialFormState: FormState = 'idle'
 | 
					 | 
				
			||||||
): [
 | 
					 | 
				
			||||||
  formState: FormState,
 | 
					 | 
				
			||||||
  setFormState: React.Dispatch<React.SetStateAction<FormState>>
 | 
					 | 
				
			||||||
] => {
 | 
					 | 
				
			||||||
  const [formState, setFormState] = useState<FormState>(initialFormState)
 | 
					 | 
				
			||||||
  return [formState, setFormState]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -19,6 +19,13 @@ export const guildCompleteSchema = {
 | 
				
			|||||||
  members: Type.Array(Type.Object(memberSchema))
 | 
					  members: Type.Array(Type.Object(memberSchema))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const guildPublicObjectSchema = Type.Object({
 | 
				
			||||||
 | 
					  ...guildSchema,
 | 
				
			||||||
 | 
					  membersCount: Type.Integer({ min: 1 })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const guildCompleteObjectSchema = Type.Object(guildCompleteSchema)
 | 
					export const guildCompleteObjectSchema = Type.Object(guildCompleteSchema)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type GuildComplete = Static<typeof guildCompleteObjectSchema>
 | 
					export type GuildComplete = Static<typeof guildCompleteObjectSchema>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type GuildPublic = Static<typeof guildPublicObjectSchema>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,10 +9,26 @@ export const userSchema = {
 | 
				
			|||||||
  name: Type.String({ minLength: 1, maxLength: 30 }),
 | 
					  name: Type.String({ minLength: 1, maxLength: 30 }),
 | 
				
			||||||
  email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }),
 | 
					  email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }),
 | 
				
			||||||
  password: Type.String({ minLength: 1 }),
 | 
					  password: Type.String({ minLength: 1 }),
 | 
				
			||||||
  logo: Type.Union([Type.String({ format: 'uri-reference' }), Type.Null()]),
 | 
					  logo: Type.Union([
 | 
				
			||||||
  status: Type.Union([Type.String({ maxLength: 50 }), Type.Null()]),
 | 
					    Type.String({ minLength: 1, format: 'uri-reference' }),
 | 
				
			||||||
  biography: Type.Union([Type.String({ maxLength: 160 }), Type.Null()]),
 | 
					    Type.Null()
 | 
				
			||||||
  website: Type.String({ maxLength: 255, format: 'uri-reference' }),
 | 
					  ]),
 | 
				
			||||||
 | 
					  status: Type.Union([
 | 
				
			||||||
 | 
					    Type.String({ minLength: 1, maxLength: 50 }),
 | 
				
			||||||
 | 
					    Type.Null()
 | 
				
			||||||
 | 
					  ]),
 | 
				
			||||||
 | 
					  biography: Type.Union([
 | 
				
			||||||
 | 
					    Type.String({ minLength: 1, maxLength: 160 }),
 | 
				
			||||||
 | 
					    Type.Null()
 | 
				
			||||||
 | 
					  ]),
 | 
				
			||||||
 | 
					  website: Type.Union([
 | 
				
			||||||
 | 
					    Type.String({
 | 
				
			||||||
 | 
					      minLength: 1,
 | 
				
			||||||
 | 
					      maxLength: 255,
 | 
				
			||||||
 | 
					      format: 'uri-reference'
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    Type.Null()
 | 
				
			||||||
 | 
					  ]),
 | 
				
			||||||
  isConfirmed: Type.Boolean({ default: false }),
 | 
					  isConfirmed: Type.Boolean({ default: false }),
 | 
				
			||||||
  temporaryToken: Type.String(),
 | 
					  temporaryToken: Type.String(),
 | 
				
			||||||
  temporaryExpirationToken: Type.String({ format: 'date-time' }),
 | 
					  temporaryExpirationToken: Type.String({ format: 'date-time' }),
 | 
				
			||||||
@@ -29,7 +45,7 @@ export const userPublicWithoutSettingsSchema = {
 | 
				
			|||||||
  logo: userSchema.logo,
 | 
					  logo: userSchema.logo,
 | 
				
			||||||
  status: userSchema.status,
 | 
					  status: userSchema.status,
 | 
				
			||||||
  biography: userSchema.biography,
 | 
					  biography: userSchema.biography,
 | 
				
			||||||
  website: Type.Union([userSchema.website, Type.Null()]),
 | 
					  website: userSchema.website,
 | 
				
			||||||
  isConfirmed: userSchema.isConfirmed,
 | 
					  isConfirmed: userSchema.isConfirmed,
 | 
				
			||||||
  createdAt: date.createdAt,
 | 
					  createdAt: date.createdAt,
 | 
				
			||||||
  updatedAt: date.updatedAt
 | 
					  updatedAt: date.updatedAt
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9705
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9705
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										65
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								package.json
									
									
									
									
									
								
							@@ -28,7 +28,7 @@
 | 
				
			|||||||
    "test:e2e:dev": "start-server-and-test 'dev' 'http://localhost:3000' 'cypress open'",
 | 
					    "test:e2e:dev": "start-server-and-test 'dev' 'http://localhost:3000' 'cypress open'",
 | 
				
			||||||
    "storybook": "start-storybook --port 6006 --static-dir public",
 | 
					    "storybook": "start-storybook --port 6006 --static-dir public",
 | 
				
			||||||
    "storybook:build": "build-storybook --static-dir public",
 | 
					    "storybook:build": "build-storybook --static-dir public",
 | 
				
			||||||
    "storybook:serve": "serve storybook-static",
 | 
					    "storybook:serve": "serve -p 6006 storybook-static",
 | 
				
			||||||
    "release": "semantic-release",
 | 
					    "release": "semantic-release",
 | 
				
			||||||
    "deploy": "vercel",
 | 
					    "deploy": "vercel",
 | 
				
			||||||
    "postinstall": "husky install"
 | 
					    "postinstall": "husky install"
 | 
				
			||||||
@@ -36,75 +36,76 @@
 | 
				
			|||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@fontsource/montserrat": "4.5.1",
 | 
					    "@fontsource/montserrat": "4.5.1",
 | 
				
			||||||
    "@fontsource/roboto": "4.5.1",
 | 
					    "@fontsource/roboto": "4.5.1",
 | 
				
			||||||
    "@heroicons/react": "1.0.4",
 | 
					    "@heroicons/react": "1.0.5",
 | 
				
			||||||
    "@sinclair/typebox": "0.20.5",
 | 
					    "@sinclair/typebox": "0.20.5",
 | 
				
			||||||
    "ajv": "8.6.3",
 | 
					    "ajv": "8.7.1",
 | 
				
			||||||
    "ajv-formats": "2.1.1",
 | 
					    "ajv-formats": "2.1.1",
 | 
				
			||||||
    "axios": "0.23.0",
 | 
					    "axios": "0.24.0",
 | 
				
			||||||
    "classnames": "2.3.1",
 | 
					    "classnames": "2.3.1",
 | 
				
			||||||
    "date-and-time": "2.0.1",
 | 
					    "date-and-time": "2.0.1",
 | 
				
			||||||
    "next": "11.1.2",
 | 
					    "next": "11.1.2",
 | 
				
			||||||
    "next-pwa": "5.3.1",
 | 
					    "next-pwa": "5.4.0",
 | 
				
			||||||
    "next-themes": "0.0.15",
 | 
					    "next-themes": "0.0.15",
 | 
				
			||||||
    "next-translate": "1.1.0",
 | 
					    "next-translate": "1.2.0",
 | 
				
			||||||
    "react": "17.0.2",
 | 
					    "react": "17.0.2",
 | 
				
			||||||
    "react-component-form": "2.0.0",
 | 
					    "react-component-form": "2.0.0",
 | 
				
			||||||
    "react-dom": "17.0.2",
 | 
					    "react-dom": "17.0.2",
 | 
				
			||||||
 | 
					    "react-infinite-scroll-component": "6.1.0",
 | 
				
			||||||
    "react-responsive": "8.2.0",
 | 
					    "react-responsive": "8.2.0",
 | 
				
			||||||
    "react-swipeable": "6.2.0",
 | 
					    "react-swipeable": "6.2.0",
 | 
				
			||||||
    "react-textarea-autosize": "8.3.3",
 | 
					    "react-textarea-autosize": "8.3.3",
 | 
				
			||||||
    "read-pkg": "7.0.0",
 | 
					    "read-pkg": "7.0.0",
 | 
				
			||||||
    "sharp": "0.29.1",
 | 
					    "sharp": "0.29.2",
 | 
				
			||||||
    "socket.io-client": "4.3.2",
 | 
					    "socket.io-client": "4.3.2",
 | 
				
			||||||
    "universal-cookie": "4.0.4"
 | 
					    "universal-cookie": "4.0.4"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@commitlint/cli": "13.2.1",
 | 
					    "@commitlint/cli": "14.1.0",
 | 
				
			||||||
    "@commitlint/config-conventional": "13.2.0",
 | 
					    "@commitlint/config-conventional": "14.1.0",
 | 
				
			||||||
    "@lhci/cli": "0.8.2",
 | 
					    "@lhci/cli": "0.8.2",
 | 
				
			||||||
    "@saithodev/semantic-release-backmerge": "1.5.3",
 | 
					    "@saithodev/semantic-release-backmerge": "2.1.0",
 | 
				
			||||||
    "@storybook/addon-essentials": "6.3.10",
 | 
					    "@storybook/addon-essentials": "6.3.12",
 | 
				
			||||||
    "@storybook/addon-links": "6.3.10",
 | 
					    "@storybook/addon-links": "6.3.12",
 | 
				
			||||||
    "@storybook/addon-postcss": "2.0.0",
 | 
					    "@storybook/addon-postcss": "2.0.0",
 | 
				
			||||||
    "@storybook/react": "6.3.10",
 | 
					    "@storybook/react": "6.3.12",
 | 
				
			||||||
    "@testing-library/jest-dom": "5.14.1",
 | 
					    "@testing-library/jest-dom": "5.15.0",
 | 
				
			||||||
    "@testing-library/react": "12.1.2",
 | 
					    "@testing-library/react": "12.1.2",
 | 
				
			||||||
    "@types/date-and-time": "0.13.0",
 | 
					    "@types/date-and-time": "0.13.0",
 | 
				
			||||||
    "@types/jest": "27.0.2",
 | 
					    "@types/jest": "27.0.2",
 | 
				
			||||||
    "@types/node": "16.10.3",
 | 
					    "@types/node": "16.11.7",
 | 
				
			||||||
    "@types/react": "17.0.27",
 | 
					    "@types/react": "17.0.34",
 | 
				
			||||||
    "@types/react-responsive": "8.0.4",
 | 
					    "@types/react-responsive": "8.0.4",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "4.33.0",
 | 
					    "@typescript-eslint/eslint-plugin": "4.33.0",
 | 
				
			||||||
    "autoprefixer": "10.3.7",
 | 
					    "autoprefixer": "10.4.0",
 | 
				
			||||||
    "babel-jest": "27.2.5",
 | 
					    "babel-jest": "27.3.1",
 | 
				
			||||||
    "babel-loader": "8.2.2",
 | 
					    "babel-loader": "8.2.3",
 | 
				
			||||||
    "babel-register": "6.26.0",
 | 
					    "babel-register": "6.26.0",
 | 
				
			||||||
    "cypress": "8.5.0",
 | 
					    "cypress": "9.0.0",
 | 
				
			||||||
    "dockerfilelint": "1.8.0",
 | 
					    "dockerfilelint": "1.8.0",
 | 
				
			||||||
    "editorconfig-checker": "4.0.2",
 | 
					    "editorconfig-checker": "4.0.2",
 | 
				
			||||||
    "eslint": "7.32.0",
 | 
					    "eslint": "7.32.0",
 | 
				
			||||||
    "eslint-config-next": "11.1.2",
 | 
					    "eslint-config-next": "11.1.2",
 | 
				
			||||||
    "eslint-config-prettier": "8.3.0",
 | 
					    "eslint-config-prettier": "8.3.0",
 | 
				
			||||||
    "eslint-config-standard-with-typescript": "21.0.1",
 | 
					    "eslint-config-standard-with-typescript": "21.0.1",
 | 
				
			||||||
    "eslint-plugin-import": "2.24.2",
 | 
					    "eslint-plugin-import": "2.25.3",
 | 
				
			||||||
    "eslint-plugin-node": "11.1.0",
 | 
					    "eslint-plugin-node": "11.1.0",
 | 
				
			||||||
    "eslint-plugin-prettier": "4.0.0",
 | 
					    "eslint-plugin-prettier": "4.0.0",
 | 
				
			||||||
    "eslint-plugin-promise": "5.1.0",
 | 
					    "eslint-plugin-promise": "5.1.1",
 | 
				
			||||||
    "eslint-plugin-unicorn": "36.0.0",
 | 
					    "eslint-plugin-unicorn": "38.0.1",
 | 
				
			||||||
    "husky": "7.0.2",
 | 
					    "husky": "7.0.4",
 | 
				
			||||||
    "jest": "27.2.5",
 | 
					    "jest": "27.3.1",
 | 
				
			||||||
    "lint-staged": "11.2.1",
 | 
					    "lint-staged": "11.2.6",
 | 
				
			||||||
    "markdownlint-cli": "0.29.0",
 | 
					    "markdownlint-cli": "0.29.0",
 | 
				
			||||||
    "mockttp": "2.3.1",
 | 
					    "mockttp": "2.4.0",
 | 
				
			||||||
    "plop": "2.7.4",
 | 
					    "plop": "2.7.6",
 | 
				
			||||||
    "postcss": "8.3.9",
 | 
					    "postcss": "8.3.11",
 | 
				
			||||||
    "prettier": "2.4.1",
 | 
					    "prettier": "2.4.1",
 | 
				
			||||||
    "semantic-release": "18.0.0",
 | 
					    "semantic-release": "18.0.0",
 | 
				
			||||||
    "serve": "12.0.1",
 | 
					    "serve": "13.0.2",
 | 
				
			||||||
    "start-server-and-test": "1.14.0",
 | 
					    "start-server-and-test": "1.14.0",
 | 
				
			||||||
    "storybook-tailwind-dark-mode": "1.0.11",
 | 
					    "storybook-tailwind-dark-mode": "1.0.11",
 | 
				
			||||||
    "tailwindcss": "2.2.16",
 | 
					    "tailwindcss": "2.2.19",
 | 
				
			||||||
    "typescript": "4.4.3",
 | 
					    "typescript": "4.4.4",
 | 
				
			||||||
    "vercel": "23.1.2"
 | 
					    "vercel": "23.1.2"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
import Image from 'next/image'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Head } from 'components/Head'
 | 
					import { Head } from 'components/Head'
 | 
				
			||||||
import { Application } from 'components/Application'
 | 
					import { Application } from 'components/Application'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@@ -7,48 +5,14 @@ import {
 | 
				
			|||||||
  AuthenticationProvider,
 | 
					  AuthenticationProvider,
 | 
				
			||||||
  PagePropsWithAuthentication
 | 
					  PagePropsWithAuthentication
 | 
				
			||||||
} from 'utils/authentication'
 | 
					} from 'utils/authentication'
 | 
				
			||||||
 | 
					import { JoinGuildsPublic } from 'components/Application/JoinGuildsPublic'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const JoinGuildPage: React.FC<PagePropsWithAuthentication> = (props) => {
 | 
					const JoinGuildPage: React.FC<PagePropsWithAuthentication> = (props) => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <AuthenticationProvider authentication={props.authentication}>
 | 
					    <AuthenticationProvider authentication={props.authentication}>
 | 
				
			||||||
      <Head title='Thream | Application' />
 | 
					      <Head title='Thream | Application' />
 | 
				
			||||||
      <Application path='/application/guilds/join'>
 | 
					      <Application path='/application/guilds/join'>
 | 
				
			||||||
        <input
 | 
					        <JoinGuildsPublic />
 | 
				
			||||||
          className='w-10/12 sm:w-8/12 md:w-6/12 lg:w-5/12 bg-white dark:bg-[#3B3B3B] border-gray-500 dark:border-gray-700 p-3 my-6 mt-16 mx-auto rounded-md border'
 | 
					 | 
				
			||||||
          type='search'
 | 
					 | 
				
			||||||
          name='search_guild'
 | 
					 | 
				
			||||||
          placeholder='🔎  Search...'
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
        <div className='w-full flex items-center justify-center p-12'>
 | 
					 | 
				
			||||||
          <div className='max-w-[1600px] grid grid-cols-1 xl:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-8'>
 | 
					 | 
				
			||||||
            {new Array(100).fill(null).map((_, index) => {
 | 
					 | 
				
			||||||
              return (
 | 
					 | 
				
			||||||
                <div
 | 
					 | 
				
			||||||
                  key={index}
 | 
					 | 
				
			||||||
                  className='max-w-sm flex flex-col items-center justify-center border-gray-500 dark:border-gray-700 p-4 cursor-pointer rounded shadow-lg border transition duration-200 ease-in-out hover:-translate-y-2 hover:shadow-none'
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  <Image
 | 
					 | 
				
			||||||
                    src='/images/icons/Thream.png'
 | 
					 | 
				
			||||||
                    alt='logo'
 | 
					 | 
				
			||||||
                    width={80}
 | 
					 | 
				
			||||||
                    height={80}
 | 
					 | 
				
			||||||
                  />
 | 
					 | 
				
			||||||
                  <div className='m-2 text-center mt-3'>
 | 
					 | 
				
			||||||
                    <h3 className='font-bold text-xl mb-2'>Guild</h3>
 | 
					 | 
				
			||||||
                    <p className='text-base w-11/12 mx-auto'>
 | 
					 | 
				
			||||||
                      Lorem ipsum dolor sit amet, consectetur adipisicing elit.
 | 
					 | 
				
			||||||
                      Voluptatibus quia, nulla! Maiores et perferendis eaque,
 | 
					 | 
				
			||||||
                      exercitationem praesentium nihil.
 | 
					 | 
				
			||||||
                    </p>
 | 
					 | 
				
			||||||
                  </div>
 | 
					 | 
				
			||||||
                  <p className='flex flex-col text-green-800 dark:text-green-400 mt-4'>
 | 
					 | 
				
			||||||
                    54 members
 | 
					 | 
				
			||||||
                  </p>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
            })}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </Application>
 | 
					      </Application>
 | 
				
			||||||
    </AuthenticationProvider>
 | 
					    </AuthenticationProvider>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ const ForgotPassword: React.FC<FooterProps> = (props) => {
 | 
				
			|||||||
  const { t } = useTranslation()
 | 
					  const { t } = useTranslation()
 | 
				
			||||||
  const { version } = props
 | 
					  const { version } = props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { formState, message, errors, getErrorTranslation, handleSubmit } =
 | 
					  const { fetchState, message, errors, getErrorTranslation, handleSubmit } =
 | 
				
			||||||
    useForm({ validateSchemaObject: { email: userSchema.email } })
 | 
					    useForm({ validateSchemaObject: { email: userSchema.email } })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onSubmit: HandleSubmitCallback = async (formData) => {
 | 
					  const onSubmit: HandleSubmitCallback = async (formData) => {
 | 
				
			||||||
@@ -65,7 +65,7 @@ const ForgotPassword: React.FC<FooterProps> = (props) => {
 | 
				
			|||||||
        </AuthenticationForm>
 | 
					        </AuthenticationForm>
 | 
				
			||||||
        <FormState
 | 
					        <FormState
 | 
				
			||||||
          id='message'
 | 
					          id='message'
 | 
				
			||||||
          state={formState}
 | 
					          state={fetchState}
 | 
				
			||||||
          message={
 | 
					          message={
 | 
				
			||||||
            message != null ? message : getErrorTranslation(errors.email)
 | 
					            message != null ? message : getErrorTranslation(errors.email)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ const ResetPassword: React.FC<FooterProps> = (props) => {
 | 
				
			|||||||
  const router = useRouter()
 | 
					  const router = useRouter()
 | 
				
			||||||
  const { version } = props
 | 
					  const { version } = props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { formState, message, errors, getErrorTranslation, handleSubmit } =
 | 
					  const { fetchState, message, errors, getErrorTranslation, handleSubmit } =
 | 
				
			||||||
    useForm({ validateSchemaObject: { password: userSchema.password } })
 | 
					    useForm({ validateSchemaObject: { password: userSchema.password } })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onSubmit: HandleSubmitCallback = async (formData) => {
 | 
					  const onSubmit: HandleSubmitCallback = async (formData) => {
 | 
				
			||||||
@@ -64,7 +64,7 @@ const ResetPassword: React.FC<FooterProps> = (props) => {
 | 
				
			|||||||
        </AuthenticationForm>
 | 
					        </AuthenticationForm>
 | 
				
			||||||
        <FormState
 | 
					        <FormState
 | 
				
			||||||
          id='message'
 | 
					          id='message'
 | 
				
			||||||
          state={formState}
 | 
					          state={fetchState}
 | 
				
			||||||
          message={
 | 
					          message={
 | 
				
			||||||
            message != null ? message : getErrorTranslation(errors.password)
 | 
					            message != null ? message : getErrorTranslation(errors.password)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user