From d8fab085855d5df673409d2f1ac6231299980a8b Mon Sep 17 00:00:00 2001 From: Divlo Date: Tue, 26 Oct 2021 16:38:55 +0200 Subject: [PATCH] feat: create a guild (#1) --- components/Application/Application.tsx | 13 +- .../CreateGuild/CreateGuild.stories.tsx | 15 ++ .../Application/CreateGuild/CreateGuild.tsx | 82 ++++++++++ components/Application/CreateGuild/index.ts | 1 + .../UserProfile/UserProfile.stories.tsx | 2 +- .../UserProfile/UserProfile.test.tsx | 2 +- .../UserProfile/UserProfile.tsx | 4 +- .../{ => Application}/UserProfile/index.ts | 0 components/Authentication/Authentication.tsx | 147 ++++++------------ cypress/fixtures/channels/channel.ts | 9 ++ cypress/fixtures/guilds/guild.ts | 8 + cypress/fixtures/guilds/post.ts | 20 +++ cypress/fixtures/members/member.ts | 16 ++ cypress/fixtures/users/user.ts | 13 +- .../pages/application/guilds/create.spec.ts | 37 +++++ .../authentication/reset-password.spec.ts | 5 +- .../useForm}/getErrorTranslationKey.test.ts | 0 .../useForm}/getErrorTranslationKey.ts | 0 hooks/useForm/index.ts | 1 + hooks/useForm/useForm.ts | 108 +++++++++++++ hooks/{useFormState.tsx => useFormState.ts} | 0 i18n.json | 5 +- locales/en/application.json | 4 +- locales/en/authentication.json | 1 - locales/en/common.json | 3 +- locales/fr/application.json | 4 +- locales/fr/authentication.json | 1 - locales/fr/common.json | 3 +- models/Channel.ts | 2 +- models/Guild.ts | 20 ++- models/Message.ts | 7 +- models/User.ts | 40 ++--- next.config.js | 6 + package-lock.json | 41 +++-- package.json | 1 + pages/application/guilds/create.tsx | 68 +------- pages/application/users/[userId].tsx | 3 +- pages/authentication/forgot-password.tsx | 67 +++----- pages/authentication/reset-password.tsx | 61 +++----- public/images/data/divlo.png | Bin 41784 -> 0 bytes public/images/data/user-default.png | Bin 0 -> 9359 bytes utils/authentication/Authentication.ts | 5 +- .../authentication/AuthenticationContext.tsx | 4 +- .../authenticationFromServerSide.ts | 14 +- utils/authentication/index.ts | 2 +- 45 files changed, 530 insertions(+), 315 deletions(-) create mode 100644 components/Application/CreateGuild/CreateGuild.stories.tsx create mode 100644 components/Application/CreateGuild/CreateGuild.tsx create mode 100644 components/Application/CreateGuild/index.ts rename components/{ => Application}/UserProfile/UserProfile.stories.tsx (84%) rename components/{ => Application}/UserProfile/UserProfile.test.tsx (81%) rename components/{ => Application}/UserProfile/UserProfile.tsx (99%) rename components/{ => Application}/UserProfile/index.ts (100%) create mode 100644 cypress/fixtures/channels/channel.ts create mode 100644 cypress/fixtures/guilds/guild.ts create mode 100644 cypress/fixtures/guilds/post.ts create mode 100644 cypress/fixtures/members/member.ts create mode 100644 cypress/integration/pages/application/guilds/create.spec.ts rename {components/Authentication => hooks/useForm}/getErrorTranslationKey.test.ts (100%) rename {components/Authentication => hooks/useForm}/getErrorTranslationKey.ts (100%) create mode 100644 hooks/useForm/index.ts create mode 100644 hooks/useForm/useForm.ts rename hooks/{useFormState.tsx => useFormState.ts} (100%) delete mode 100644 public/images/data/divlo.png create mode 100644 public/images/data/user-default.png diff --git a/components/Application/Application.tsx b/components/Application/Application.tsx index 944f74f..e8214c2 100644 --- a/components/Application/Application.tsx +++ b/components/Application/Application.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useMemo } from 'react' import Image from 'next/image' +import useTranslation from 'next-translate/useTranslation' import { CogIcon, PlusIcon, @@ -19,6 +20,7 @@ import { Guilds } from './Guilds/Guilds' import { Divider } from '../design/Divider' import { Members } from './Members' import { useAuthentication } from 'utils/authentication' +import { API_URL } from 'utils/api' export interface GuildsChannelsPath { guildId: number @@ -37,6 +39,7 @@ export interface ApplicationProps { export const Application: React.FC = (props) => { const { children, path } = props + const { t } = useTranslation() const { user } = useAuthentication() const [visibleSidebars, setVisibleSidebars] = useState({ @@ -138,10 +141,10 @@ export const Application: React.FC = (props) => { return 'Join a Guild' } if (path === '/application/guilds/create') { - return 'Create a Guild' + return t('application:create-a-guild') } return 'Application' - }, [path]) + }, [path, t]) useEffect(() => { setMounted(true) @@ -193,7 +196,11 @@ export const Application: React.FC = (props) => { > logo { + return +} +CreateGuild.args = {} diff --git a/components/Application/CreateGuild/CreateGuild.tsx b/components/Application/CreateGuild/CreateGuild.tsx new file mode 100644 index 0000000..b2fd563 --- /dev/null +++ b/components/Application/CreateGuild/CreateGuild.tsx @@ -0,0 +1,82 @@ +import { useRouter } from 'next/router' +import useTranslation from 'next-translate/useTranslation' +import { Form } from 'react-component-form' +import TextareaAutosize from 'react-textarea-autosize' +import { AxiosResponse } from 'axios' + +import { useAuthentication } from '../../../utils/authentication' +import { HandleSubmitCallback, useForm } from '../../../hooks/useForm' +import { GuildComplete, guildSchema } from '../../../models/Guild' +import { Input } from '../../design/Input' +import { Main } from '../../design/Main' +import { Button } from '../../design/Button' +import { FormState } from '../../design/FormState' + +export const CreateGuild: React.FC = () => { + const { t } = useTranslation() + const router = useRouter() + + const { formState, message, errors, getErrorTranslation, handleSubmit } = + useForm({ + validateSchemaObject: { + name: guildSchema.name, + description: guildSchema.description + } + }) + + const { authentication } = useAuthentication() + + const onSubmit: HandleSubmitCallback = async (formData) => { + try { + const { data } = await authentication.api.post< + any, + AxiosResponse<{ guild: GuildComplete }> + >('/guilds', { name: formData.name, description: formData.description }) + const guildId = data.guild.id + const channelId = data.guild.channels[0].id + await router.push(`/application/${guildId}/${channelId}`) + return null + } catch (error) { + return { + type: 'error', + value: 'errors:server-error' + } + } + } + + return ( +
+
+ + +
+
+ +
+
+ +
+
+ + +
+ +
+ ) +} diff --git a/components/Application/CreateGuild/index.ts b/components/Application/CreateGuild/index.ts new file mode 100644 index 0000000..cc03400 --- /dev/null +++ b/components/Application/CreateGuild/index.ts @@ -0,0 +1 @@ +export * from './CreateGuild' diff --git a/components/UserProfile/UserProfile.stories.tsx b/components/Application/UserProfile/UserProfile.stories.tsx similarity index 84% rename from components/UserProfile/UserProfile.stories.tsx rename to components/Application/UserProfile/UserProfile.stories.tsx index e8e6953..ee76ced 100644 --- a/components/UserProfile/UserProfile.stories.tsx +++ b/components/Application/UserProfile/UserProfile.stories.tsx @@ -1,5 +1,5 @@ import { Meta, Story } from '@storybook/react' -import { user, userSettings } from '../../cypress/fixtures/users/user' +import { user, userSettings } from '../../../cypress/fixtures/users/user' import { UserProfile as Component, UserProfileProps } from './UserProfile' diff --git a/components/UserProfile/UserProfile.test.tsx b/components/Application/UserProfile/UserProfile.test.tsx similarity index 81% rename from components/UserProfile/UserProfile.test.tsx rename to components/Application/UserProfile/UserProfile.test.tsx index fa7d128..08a07a7 100644 --- a/components/UserProfile/UserProfile.test.tsx +++ b/components/Application/UserProfile/UserProfile.test.tsx @@ -1,6 +1,6 @@ import { render } from '@testing-library/react' -import { user, userSettings } from '../../cypress/fixtures/users/user' +import { user, userSettings } from '../../../cypress/fixtures/users/user' import { UserProfile } from './UserProfile' diff --git a/components/UserProfile/UserProfile.tsx b/components/Application/UserProfile/UserProfile.tsx similarity index 99% rename from components/UserProfile/UserProfile.tsx rename to components/Application/UserProfile/UserProfile.tsx index c1bb81f..38a6f04 100644 --- a/components/UserProfile/UserProfile.tsx +++ b/components/Application/UserProfile/UserProfile.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames' import useTranslation from 'next-translate/useTranslation' import date from 'date-and-time' -import { UserPublic } from '../../models/User' +import { UserPublic } from '../../../models/User' export interface UserProfileProps { className?: string @@ -15,8 +15,6 @@ export interface UserProfileProps { export const UserProfile: React.FC = (props) => { const { user, isOwner = false } = props - console.log(user) - const { t } = useTranslation() const handleSubmitChanges = ( diff --git a/components/UserProfile/index.ts b/components/Application/UserProfile/index.ts similarity index 100% rename from components/UserProfile/index.ts rename to components/Application/UserProfile/index.ts diff --git a/components/Authentication/Authentication.tsx b/components/Authentication/Authentication.tsx index 587b48d..d1621ac 100644 --- a/components/Authentication/Authentication.tsx +++ b/components/Authentication/Authentication.tsx @@ -1,11 +1,7 @@ -import { useMemo, useState } from 'react' import { useRouter } from 'next/router' import Link from 'next/link' import useTranslation from 'next-translate/useTranslation' import { useTheme } from 'next-themes' -import { Type } from '@sinclair/typebox' -import type { ErrorObject } from 'ajv' -import type { HandleForm } from 'react-component-form' import axios from 'axios' import { SocialMediaButton } from '../design/SocialMediaButton' @@ -13,26 +9,14 @@ import { Main } from '../design/Main' import { Input } from '../design/Input' import { Button } from '../design/Button' import { FormState } from '../design/FormState' -import { useFormState } from '../../hooks/useFormState' import { AuthenticationForm } from './' import { userSchema } from '../../models/User' -import { ajv } from '../../utils/ajv' import { api } from 'utils/api' import { Tokens, Authentication as AuthenticationClass } from '../../utils/authentication' -import { getErrorTranslationKey } from './getErrorTranslationKey' - -interface Errors { - [key: string]: ErrorObject | null | undefined -} - -const findError = ( - field: string -): ((value: ErrorObject, index: number, object: ErrorObject[]) => boolean) => { - return (validationError) => validationError.instancePath === field -} +import { useForm, HandleSubmitCallback } from '../../hooks/useForm' export interface AuthenticationProps { mode: 'signup' | 'signin' @@ -44,84 +28,57 @@ export const Authentication: React.FC = (props) => { const router = useRouter() const { lang, t } = useTranslation() const { theme } = useTheme() - const [formState, setFormState] = useFormState() - const [messageTranslationKey, setMessageTranslationKey] = useState< - string | undefined - >(undefined) - const [errors, setErrors] = useState({ - name: null, - email: null, - password: null - }) - const validateSchema = useMemo(() => { - return Type.Object({ - ...(mode === 'signup' && { name: userSchema.name }), - email: userSchema.email, - password: userSchema.password + const { errors, formState, message, getErrorTranslation, handleSubmit } = + useForm({ + validateSchemaObject: { + ...(mode === 'signup' && { name: userSchema.name }), + email: userSchema.email, + password: userSchema.password + } }) - }, [mode]) - const validate = useMemo(() => { - return ajv.compile(validateSchema) - }, [validateSchema]) - - const getErrorTranslation = (error?: ErrorObject | null): string | null => { - if (error != null) { - return t(getErrorTranslationKey(error)).replace( - '{expected}', - error?.params?.limit - ) - } - return null - } - - const handleSubmit: HandleForm = async (formData, formElement) => { - const isValid = validate(formData) - if (!isValid) { - setFormState('error') - const nameError = validate?.errors?.find(findError('/name')) - const emailError = validate?.errors?.find(findError('/email')) - const passwordError = validate?.errors?.find(findError('/password')) - setErrors({ - name: nameError, - email: emailError, - password: passwordError - }) - } else { - setErrors({}) - setFormState('loading') - if (mode === 'signup') { - try { - await api.post( - `/users/signup?redirectURI=${window.location.origin}/authentication/signin`, - { ...formData, language: lang, theme } - ) - formElement.reset() - setFormState('success') - setMessageTranslationKey('authentication:success-signup') - } catch (error) { - setFormState('error') - if (axios.isAxiosError(error) && error.response?.status === 400) { - setMessageTranslationKey('authentication:alreadyUsed') - } else { - setMessageTranslationKey('errors:server-error') + const onSubmit: HandleSubmitCallback = async (formData) => { + if (mode === 'signup') { + try { + await api.post( + `/users/signup?redirectURI=${window.location.origin}/authentication/signin`, + { ...formData, language: lang, theme } + ) + return { + type: 'success', + value: 'authentication:success-signup' + } + } catch (error) { + if (axios.isAxiosError(error) && error.response?.status === 400) { + return { + type: 'error', + value: 'authentication:alreadyUsed' } } - } else { - try { - const { data } = await api.post('/users/signin', formData) - const authentication = new AuthenticationClass(data) - authentication.signin() - await router.push('/application') - } catch (error) { - setFormState('error') - if (axios.isAxiosError(error) && error.response?.status === 400) { - setMessageTranslationKey('authentication:wrong-credentials') - } else { - setMessageTranslationKey('errors:server-error') + return { + type: 'error', + value: 'errors:server-error' + } + } + } else { + try { + const { data } = await api.post('/users/signin', formData) + const authentication = new AuthenticationClass(data) + authentication.signin() + await router.push('/application') + return null + } catch (error) { + if (axios.isAxiosError(error) && error.response?.status === 400) { + return { + type: 'error', + value: 'authentication:wrong-credentials' } } + return { + type: 'error', + value: 'errors:server-error' + } } } } @@ -138,13 +95,13 @@ export const Authentication: React.FC = (props) => {
{t('authentication:or')}
- + {mode === 'signup' && ( )} @@ -182,13 +139,7 @@ export const Authentication: React.FC = (props) => {

- + ) } diff --git a/cypress/fixtures/channels/channel.ts b/cypress/fixtures/channels/channel.ts new file mode 100644 index 0000000..dc221f3 --- /dev/null +++ b/cypress/fixtures/channels/channel.ts @@ -0,0 +1,9 @@ +import { guild } from '../guilds/guild' + +export const channel = { + id: 1, + name: 'general', + guildId: guild.id, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() +} diff --git a/cypress/fixtures/guilds/guild.ts b/cypress/fixtures/guilds/guild.ts new file mode 100644 index 0000000..bcd9a50 --- /dev/null +++ b/cypress/fixtures/guilds/guild.ts @@ -0,0 +1,8 @@ +export const guild = { + id: 1, + name: 'GuildExample', + description: 'guild example.', + icon: null, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() +} diff --git a/cypress/fixtures/guilds/post.ts b/cypress/fixtures/guilds/post.ts new file mode 100644 index 0000000..8feefa3 --- /dev/null +++ b/cypress/fixtures/guilds/post.ts @@ -0,0 +1,20 @@ +import { Handler } from '../handler' + +import { guild } from './guild' +import { channel } from '../channels/channel' +import { memberComplete } from '../members/member' + +export const postGuildsHandler: Handler = { + method: 'POST', + url: '/guilds', + response: { + statusCode: 201, + body: { + guild: { + ...guild, + channels: [channel], + members: [memberComplete] + } + } + } +} diff --git a/cypress/fixtures/members/member.ts b/cypress/fixtures/members/member.ts new file mode 100644 index 0000000..46872c7 --- /dev/null +++ b/cypress/fixtures/members/member.ts @@ -0,0 +1,16 @@ +import { guild } from '../guilds/guild' +import { user } from '../users/user' + +export const member = { + id: 1, + isOwner: true, + userId: user.id, + guildId: guild.id, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() +} + +export const memberComplete = { + ...member, + user +} diff --git a/cypress/fixtures/users/user.ts b/cypress/fixtures/users/user.ts index 0024a65..d43dcb9 100644 --- a/cypress/fixtures/users/user.ts +++ b/cypress/fixtures/users/user.ts @@ -1,15 +1,18 @@ import { UserSettings } from '../../../models/UserSettings' -import { UserPublic } from '../../../models/User' +import { User } from '../../../models/User' -export const user: UserPublic = { +export const user: User = { id: 1, name: 'Divlo', email: 'contact@divlo.fr', - logo: undefined, - status: undefined, - biography: undefined, + password: 'somepassword', + logo: null, + status: null, + biography: null, website: 'https://divlo.fr', isConfirmed: true, + temporaryToken: 'temporaryUUIDtoken', + temporaryExpirationToken: '2021-10-20T20:59:08.485Z', createdAt: '2021-10-20T20:30:51.595Z', updatedAt: '2021-10-20T20:59:08.485Z' } diff --git a/cypress/integration/pages/application/guilds/create.spec.ts b/cypress/integration/pages/application/guilds/create.spec.ts new file mode 100644 index 0000000..e276376 --- /dev/null +++ b/cypress/integration/pages/application/guilds/create.spec.ts @@ -0,0 +1,37 @@ +import { channel } from '../../../../fixtures/channels/channel' +import { guild } from '../../../../fixtures/guilds/guild' +import { postGuildsHandler } from '../../../../fixtures/guilds/post' +import { authenticationHandlers } from '../../../../fixtures/handler' + +describe('Pages > /application/guilds/create', () => { + beforeEach(() => { + cy.task('stopMockServer') + }) + + it('should succeeds and create the guild', () => { + cy.task('startMockServer', [ + ...authenticationHandlers, + postGuildsHandler + ]).setCookie('refreshToken', 'refresh-token') + cy.visit('/application/guilds/create') + cy.get('#error-name').should('not.exist') + cy.get('[data-cy=input-name]').type(guild.name) + cy.get('[data-cy=submit]').click() + cy.location('pathname').should( + 'eq', + `/application/${guild.id}/${channel.id}` + ) + }) + + it('should fails with internal api server error', () => { + cy.task('startMockServer', [...authenticationHandlers]).setCookie( + 'refreshToken', + 'refresh-token' + ) + cy.visit('/application/guilds/create') + cy.get('#error-name').should('not.exist') + cy.get('[data-cy=input-name]').type(guild.name) + cy.get('[data-cy=submit]').click() + cy.get('#message').should('have.text', 'Error: Internal Server Error.') + }) +}) diff --git a/cypress/integration/pages/authentication/reset-password.spec.ts b/cypress/integration/pages/authentication/reset-password.spec.ts index 7195389..74e1460 100644 --- a/cypress/integration/pages/authentication/reset-password.spec.ts +++ b/cypress/integration/pages/authentication/reset-password.spec.ts @@ -40,6 +40,9 @@ describe('Pages > /authentication/reset-password', () => { cy.visit('/authentication/reset-password') cy.get('#message').should('not.exist') cy.get('[data-cy=submit]').click() - cy.get('#message').should('have.text', 'Error: Invalid value.') + cy.get('#message').should( + 'have.text', + 'Error: Oops, this field is required 🙈.' + ) }) }) diff --git a/components/Authentication/getErrorTranslationKey.test.ts b/hooks/useForm/getErrorTranslationKey.test.ts similarity index 100% rename from components/Authentication/getErrorTranslationKey.test.ts rename to hooks/useForm/getErrorTranslationKey.test.ts diff --git a/components/Authentication/getErrorTranslationKey.ts b/hooks/useForm/getErrorTranslationKey.ts similarity index 100% rename from components/Authentication/getErrorTranslationKey.ts rename to hooks/useForm/getErrorTranslationKey.ts diff --git a/hooks/useForm/index.ts b/hooks/useForm/index.ts new file mode 100644 index 0000000..f65863c --- /dev/null +++ b/hooks/useForm/index.ts @@ -0,0 +1 @@ +export * from './useForm' diff --git a/hooks/useForm/useForm.ts b/hooks/useForm/useForm.ts new file mode 100644 index 0000000..c3489c2 --- /dev/null +++ b/hooks/useForm/useForm.ts @@ -0,0 +1,108 @@ +import { useMemo, useState } from 'react' +import useTranslation from 'next-translate/useTranslation' +import { Type } from '@sinclair/typebox' +import type { FormDataObject, HandleForm } from 'react-component-form' +import type { ErrorObject } from 'ajv' + +import { FormState, useFormState } from '../useFormState' +import { ajv } from '../../utils/ajv' +import { getErrorTranslationKey } from './getErrorTranslationKey' + +interface Errors { + [key: string]: ErrorObject | null | undefined +} + +const findError = ( + field: string +): ((value: ErrorObject, index: number, object: ErrorObject[]) => boolean) => { + return (validationError) => validationError.instancePath === field +} + +export type GetErrorTranslation = (error?: ErrorObject | null) => string | null + +export interface UseFormOptions { + validateSchemaObject: { [key: string]: any } +} + +export type HandleSubmit = (callback: HandleSubmitCallback) => HandleForm + +interface Message { + type: 'error' | 'success' + value: string +} + +export type HandleSubmitCallback = ( + formData: FormDataObject, + formElement: HTMLFormElement +) => Promise + +export interface UseFormResult { + message: string | null + formState: FormState + getErrorTranslation: GetErrorTranslation + handleSubmit: HandleSubmit + errors: Errors +} + +export const useForm = (options: UseFormOptions): UseFormResult => { + const { validateSchemaObject } = options + const { t } = useTranslation() + const [formState, setFormState] = useFormState() + const [messageTranslationKey, setMessageTranslationKey] = useState< + string | undefined + >(undefined) + const [errors, setErrors] = useState({}) + + const validateSchema = useMemo(() => { + return Type.Object(validateSchemaObject) + }, [validateSchemaObject]) + + const validate = useMemo(() => { + return ajv.compile(validateSchema) + }, [validateSchema]) + + const getErrorTranslation = (error?: ErrorObject | null): string | null => { + if (error != null) { + return t(getErrorTranslationKey(error)).replace( + '{expected}', + error?.params?.limit + ) + } + return null + } + + const handleSubmit: HandleSubmit = (callback) => { + return async (formData, formElement) => { + const isValid = validate(formData) + if (!isValid) { + setFormState('error') + const errors: Errors = {} + for (const property in validateSchema.properties) { + errors[property] = validate.errors?.find(findError(`/${property}`)) + } + setErrors(errors) + } else { + setErrors({}) + setFormState('loading') + const message = await callback(formData, formElement) + if (message != null) { + setMessageTranslationKey(message.value) + if (message.type === 'success') { + setFormState('success') + formElement.reset() + } else { + setFormState('error') + } + } + } + } + } + + return { + getErrorTranslation, + errors, + formState, + handleSubmit, + message: messageTranslationKey != null ? t(messageTranslationKey) : null + } +} diff --git a/hooks/useFormState.tsx b/hooks/useFormState.ts similarity index 100% rename from hooks/useFormState.tsx rename to hooks/useFormState.ts diff --git a/i18n.json b/i18n.json index b2e0b17..e1a9a74 100644 --- a/i18n.json +++ b/i18n.json @@ -10,6 +10,9 @@ "/authentication/reset-password": ["authentication", "errors"], "/authentication/signup": ["authentication", "errors"], "/authentication/signin": ["authentication", "errors"], - "/application/users/[userId]": ["application"] + "/application/users/[userId]": ["application", "errors"], + "/application/guilds/create": ["application", "errors"], + "/application/guilds/join": ["application", "errors"], + "/application": ["application", "errors"] } } diff --git a/locales/en/application.json b/locales/en/application.json index 637074e..84f0ec5 100644 --- a/locales/en/application.json +++ b/locales/en/application.json @@ -1,3 +1,5 @@ { - "website": "Website" + "website": "Website", + "create": "Create", + "create-a-guild": "Create a Guild" } diff --git a/locales/en/authentication.json b/locales/en/authentication.json index 56909e7..7febab8 100644 --- a/locales/en/authentication.json +++ b/locales/en/authentication.json @@ -1,7 +1,6 @@ { "or": "OR", "password": "Password", - "name": "Name", "already-have-an-account": "Already have an account?", "dont-have-an-account": "Don't have an account?", "submit": "Submit", diff --git a/locales/en/common.json b/locales/en/common.json index d5bd8e4..95b71f3 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -2,5 +2,6 @@ "english": "English", "french": "French", "all-rights-reserved": "All rights reserved", - "description": "Stay close with your friends and communities, talk, chat, collaborate, share, and have fun." + "description": "Stay close with your friends and communities, talk, chat, collaborate, share, and have fun.", + "name": "Name" } diff --git a/locales/fr/application.json b/locales/fr/application.json index ca95713..c1af9f5 100644 --- a/locales/fr/application.json +++ b/locales/fr/application.json @@ -1,3 +1,5 @@ { - "website": "Site web" + "website": "Site web", + "create": "CrĂ©e", + "create-a-guild": "CrĂ©e une Guilde" } diff --git a/locales/fr/authentication.json b/locales/fr/authentication.json index 4fac5ad..9db8638 100644 --- a/locales/fr/authentication.json +++ b/locales/fr/authentication.json @@ -1,7 +1,6 @@ { "or": "OU", "password": "Mot de passe", - "name": "Nom", "already-have-an-account": "Vous avez dĂ©jĂ  un compte ?", "dont-have-an-account": "Vous n'avez pas de compte ?", "submit": "Soumettre", diff --git a/locales/fr/common.json b/locales/fr/common.json index a5980d4..0c3eae3 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -2,5 +2,6 @@ "english": "Anglais", "french": "Français", "all-rights-reserved": "Tous droits rĂ©servĂ©s", - "description": "Restez proche de vos amis et de vos communautĂ©s, parlez, collaborez, partagez et amusez-vous." + "description": "Restez proche de vos amis et de vos communautĂ©s, parlez, collaborez, partagez et amusez-vous.", + "name": "Nom" } diff --git a/models/Channel.ts b/models/Channel.ts index eb21334..4246744 100644 --- a/models/Channel.ts +++ b/models/Channel.ts @@ -6,7 +6,7 @@ export const types = [Type.Literal('text')] export const channelSchema = { id, - name: Type.String({ maxLength: 255 }), + name: Type.String({ minLength: 1, maxLength: 20 }), createdAt: date.createdAt, updatedAt: date.updatedAt, guildId: id diff --git a/models/Guild.ts b/models/Guild.ts index ee821fd..e1b6522 100644 --- a/models/Guild.ts +++ b/models/Guild.ts @@ -1,12 +1,24 @@ -import { Type } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' +import { channelSchema } from './Channel' +import { memberSchema } from './Member' import { date, id } from './utils' export const guildSchema = { id, - name: Type.String({ minLength: 3, maxLength: 30 }), - icon: Type.String({ format: 'uri-reference' }), - description: Type.String({ maxLength: 160 }), + name: Type.String({ minLength: 1, maxLength: 30 }), + icon: Type.Union([Type.String({ format: 'uri-reference' }), Type.Null()]), + description: Type.Union([Type.String({ maxLength: 160 }), Type.Null()]), createdAt: date.createdAt, updatedAt: date.updatedAt } + +export const guildCompleteSchema = { + ...guildSchema, + channels: Type.Array(Type.Object(channelSchema)), + members: Type.Array(Type.Object(memberSchema)) +} + +export const guildCompleteObjectSchema = Type.Object(guildCompleteSchema) + +export type GuildComplete = Static diff --git a/models/Message.ts b/models/Message.ts index e45b15c..c8a0d7a 100644 --- a/models/Message.ts +++ b/models/Message.ts @@ -6,10 +6,13 @@ export const types = [Type.Literal('text'), Type.Literal('file')] export const messageSchema = { id, - value: Type.String(), + value: Type.String({ + minLength: 1, + maxLength: 20_000 + }), type: Type.Union(types, { default: 'text' }), mimetype: Type.String({ - maxLength: 255, + maxLength: 127, default: 'text/plain', format: 'mimetype' }), diff --git a/models/User.ts b/models/User.ts index 812837e..4884aa4 100644 --- a/models/User.ts +++ b/models/User.ts @@ -7,11 +7,11 @@ import { date, id } from './utils' export const userSchema = { id, name: Type.String({ minLength: 1, maxLength: 30 }), - email: Type.String({ minLength: 1, maxLength: 255, format: 'email' }), + email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }), password: Type.String({ minLength: 1 }), - logo: Type.String({ format: 'uri-reference' }), - status: Type.String({ maxLength: 255 }), - biography: Type.String(), + logo: Type.Union([Type.String({ format: 'uri-reference' }), Type.Null()]), + status: Type.Union([Type.String({ maxLength: 50 }), Type.Null()]), + biography: Type.Union([Type.String({ maxLength: 160 }), Type.Null()]), website: Type.String({ maxLength: 255, format: 'uri-reference' }), isConfirmed: Type.Boolean({ default: false }), temporaryToken: Type.String(), @@ -20,32 +20,34 @@ export const userSchema = { updatedAt: date.updatedAt } -const userSchemaWithSettings = { - ...userSchema, - settings: Type.Object(userSettingsSchema) +export const userObjectSchema = Type.Object(userSchema) + +export const userPublicWithoutSettingsSchema = { + id, + name: userSchema.name, + email: Type.Union([userSchema.email, Type.Null()]), + logo: userSchema.logo, + status: userSchema.status, + biography: userSchema.biography, + website: Type.Union([userSchema.website, Type.Null()]), + isConfirmed: userSchema.isConfirmed, + createdAt: date.createdAt, + updatedAt: date.updatedAt } export const userPublicSchema = { - id, - name: userSchema.name, - email: Type.Optional(userSchema.email), - logo: Type.Optional(userSchema.logo), - status: Type.Optional(userSchema.status), - biography: Type.Optional(userSchema.biography), - website: Type.Optional(userSchema.website), - isConfirmed: userSchema.isConfirmed, - createdAt: date.createdAt, - updatedAt: date.updatedAt, - settings: Type.Optional(Type.Object(userSettingsSchema)) + ...userPublicWithoutSettingsSchema, + settings: Type.Object(userSettingsSchema) } export const userPublicObjectSchema = Type.Object(userPublicSchema) export const userCurrentSchema = Type.Object({ - ...userSchemaWithSettings, + ...userPublicSchema, currentStrategy: Type.Union([...strategiesTypebox]), strategies: Type.Array(Type.Union([...strategiesTypebox])) }) +export type User = Static export type UserPublic = Static export type UserCurrent = Static diff --git a/next.config.js b/next.config.js index 4cdf79a..c22e29c 100644 --- a/next.config.js +++ b/next.config.js @@ -6,6 +6,12 @@ module.exports = nextTranslate( pwa: { disable: process.env.NODE_ENV !== 'production', dest: 'public' + }, + images: { + domains: [ + 'api.thream.divlo.fr', + ...(process.env.NODE_ENV === 'development' ? ['localhost'] : []) + ] } }) ) diff --git a/package-lock.json b/package-lock.json index 7690617..9591cdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@sinclair/typebox": "0.20.5", "ajv": "8.6.3", "ajv-formats": "2.1.1", + "axios": "0.23.0", "classnames": "2.3.1", "date-and-time": "2.0.1", "next": "11.1.2", @@ -8388,12 +8389,11 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dev": true, + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.23.0.tgz", + "integrity": "sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==", "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.4" } }, "node_modules/axobject-query": { @@ -16214,7 +16214,6 @@ "version": "1.14.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", - "dev": true, "funding": [ { "type": "individual", @@ -37731,6 +37730,15 @@ "node": ">=10.0.0" } }, + "node_modules/wait-on/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/wait-on/node_modules/rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", @@ -45698,12 +45706,11 @@ "dev": true }, "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dev": true, + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.23.0.tgz", + "integrity": "sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==", "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.4" } }, "axobject-query": { @@ -51976,8 +51983,7 @@ "follow-redirects": { "version": "1.14.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", - "dev": true + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==" }, "for-in": { "version": "1.0.2", @@ -68666,6 +68672,15 @@ "rxjs": "^7.1.0" }, "dependencies": { + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.0" + } + }, "rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", diff --git a/package.json b/package.json index 966e90a..a0e7661 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@sinclair/typebox": "0.20.5", "ajv": "8.6.3", "ajv-formats": "2.1.1", + "axios": "0.23.0", "classnames": "2.3.1", "date-and-time": "2.0.1", "next": "11.1.2", diff --git a/pages/application/guilds/create.tsx b/pages/application/guilds/create.tsx index 60182aa..663bd45 100644 --- a/pages/application/guilds/create.tsx +++ b/pages/application/guilds/create.tsx @@ -1,77 +1,19 @@ -import Image from 'next/image' -import { Form } from 'react-component-form' -import TextareaAutosize from 'react-textarea-autosize' - import { Head } from 'components/Head' import { Application } from 'components/Application' -import { Input } from 'components/design/Input' -import { Main } from 'components/design/Main' -import { Button } from 'components/design/Button' -import { FormState } from 'components/design/FormState' + import { authenticationFromServerSide, AuthenticationProvider, PagePropsWithAuthentication } from 'utils/authentication' +import { CreateGuild } from 'components/Application/CreateGuild' -const CreateGuild: React.FC = (props) => { +const CreateGuildPage: React.FC = (props) => { return ( -
-
-
-
- -
- -
- -
-
- - - -
-
- -
-
- - -
-
- - -
-
+
) @@ -81,4 +23,4 @@ export const getServerSideProps = authenticationFromServerSide({ shouldBeAuthenticated: true }) -export default CreateGuild +export default CreateGuildPage diff --git a/pages/application/users/[userId].tsx b/pages/application/users/[userId].tsx index 3245364..5ca68a9 100644 --- a/pages/application/users/[userId].tsx +++ b/pages/application/users/[userId].tsx @@ -5,8 +5,7 @@ import { AuthenticationProvider, PagePropsWithAuthentication } from 'utils/authentication' - -import { UserProfile } from 'components/UserProfile' +import { UserProfile } from 'components/Application/UserProfile' const UserProfilePage: React.FC = (props) => { return ( diff --git a/pages/authentication/forgot-password.tsx b/pages/authentication/forgot-password.tsx index 255a271..7344a30 100644 --- a/pages/authentication/forgot-password.tsx +++ b/pages/authentication/forgot-password.tsx @@ -1,10 +1,7 @@ -import { useState, useMemo } from 'react' import Link from 'next/link' import { AuthenticationForm } from 'components/Authentication' import useTranslation from 'next-translate/useTranslation' -import { HandleForm } from 'react-component-form' import axios from 'axios' -import { Type } from '@sinclair/typebox' import { Head } from 'components/Head' import { Header } from 'components/Header' @@ -13,54 +10,40 @@ import { Footer, FooterProps } from 'components/Footer' import { Input } from 'components/design/Input' import { Button } from 'components/design/Button' import { FormState } from 'components/design/FormState' -import { useFormState } from 'hooks/useFormState' import { authenticationFromServerSide } from 'utils/authentication' import { ScrollableBody } from 'components/ScrollableBody' +import { userSchema } from 'models/User' import { api } from 'utils/api' -import { userSchema } from '../../models/User' -import { ajv } from '../../utils/ajv' +import { HandleSubmitCallback, useForm } from 'hooks/useForm' const ForgotPassword: React.FC = (props) => { const { t } = useTranslation() const { version } = props - const [formState, setFormState] = useFormState() - const [messageTranslationKey, setMessageTranslationKey] = useState< - string | undefined - >(undefined) - const validateSchema = useMemo(() => { - return Type.Object({ - email: userSchema.email - }) - }, []) + const { formState, message, errors, getErrorTranslation, handleSubmit } = + useForm({ validateSchemaObject: { email: userSchema.email } }) - const validate = useMemo(() => { - return ajv.compile(validateSchema) - }, [validateSchema]) - - const handleSubmit: HandleForm = async (formData, formElement) => { - const isValid = validate(formData) - if (!isValid) { - setFormState('error') - setMessageTranslationKey('errors:email') - } else { - setFormState('loading') - try { - await api.post( - `/users/reset-password?redirectURI=${window.location.origin}/authentication/reset-password`, - formData - ) - formElement.reset() - setFormState('success') - setMessageTranslationKey('authentication:success-forgot-password') - } catch (error) { - setFormState('error') - if (axios.isAxiosError(error) && error.response?.status === 400) { - setMessageTranslationKey('errors:email') - } else { - setMessageTranslationKey('errors:server-error') + const onSubmit: HandleSubmitCallback = async (formData) => { + try { + await api.post( + `/users/reset-password?redirectURI=${window.location.origin}/authentication/reset-password`, + formData + ) + return { + type: 'success', + value: 'authentication:success-forgot-password' + } + } catch (error) { + if (axios.isAxiosError(error) && error.response?.status === 400) { + return { + type: 'error', + value: 'errors:email' } } + return { + type: 'error', + value: 'errors:server-error' + } } } @@ -69,7 +52,7 @@ const ForgotPassword: React.FC = (props) => {
- +
diff --git a/pages/authentication/reset-password.tsx b/pages/authentication/reset-password.tsx index 6aabc68..8d21710 100644 --- a/pages/authentication/reset-password.tsx +++ b/pages/authentication/reset-password.tsx @@ -1,9 +1,6 @@ -import { useState, useMemo } from 'react' import { useRouter } from 'next/router' import useTranslation from 'next-translate/useTranslation' -import { HandleForm } from 'react-component-form' import axios from 'axios' -import { Type } from '@sinclair/typebox' import { Head } from 'components/Head' import { Header } from 'components/Header' @@ -12,54 +9,40 @@ import { Footer, FooterProps } from 'components/Footer' import { Input } from 'components/design/Input' import { Button } from 'components/design/Button' import { FormState } from 'components/design/FormState' -import { useFormState } from 'hooks/useFormState' import { authenticationFromServerSide } from 'utils/authentication' import { AuthenticationForm } from 'components/Authentication' import { ScrollableBody } from 'components/ScrollableBody/ScrollableBody' import { api } from 'utils/api' import { userSchema } from '../../models/User' -import { ajv } from '../../utils/ajv' +import { HandleSubmitCallback, useForm } from 'hooks/useForm' const ResetPassword: React.FC = (props) => { const { t } = useTranslation() const router = useRouter() const { version } = props - const [formState, setFormState] = useFormState() - const [messageTranslationKey, setMessageTranslationKey] = useState< - string | undefined - >(undefined) - const validateSchema = useMemo(() => { - return Type.Object({ - password: userSchema.password - }) - }, []) + const { formState, message, errors, getErrorTranslation, handleSubmit } = + useForm({ validateSchemaObject: { password: userSchema.password } }) - const validate = useMemo(() => { - return ajv.compile(validateSchema) - }, [validateSchema]) - - const handleSubmit: HandleForm = async (formData, formElement) => { - const isValid = validate(formData) - if (!isValid) { - setFormState('error') - setMessageTranslationKey('errors:invalid') - } else { - setFormState('loading') - try { - await api.put(`/users/reset-password`, { - ...formData, - temporaryToken: router.query.temporaryToken - }) - await router.push('/authentication/signin') - } catch (error) { - setFormState('error') - if (axios.isAxiosError(error) && error.response?.status === 400) { - setMessageTranslationKey('errors:invalid') - } else { - setMessageTranslationKey('errors:server-error') + const onSubmit: HandleSubmitCallback = async (formData) => { + try { + await api.put(`/users/reset-password`, { + ...formData, + temporaryToken: router.query.temporaryToken + }) + await router.push('/authentication/signin') + return null + } catch (error) { + if (axios.isAxiosError(error) && error.response?.status === 400) { + return { + type: 'error', + value: 'errors:invalid' } } + return { + type: 'error', + value: 'errors:server-error' + } } } @@ -68,7 +51,7 @@ const ResetPassword: React.FC = (props) => {
- + = (props) => { id='message' state={formState} message={ - messageTranslationKey != null ? t(messageTranslationKey) : null + message != null ? message : getErrorTranslation(errors.password) } />
diff --git a/public/images/data/divlo.png b/public/images/data/divlo.png deleted file mode 100644 index 06bb3472830c2242e2223342b9f84b89711d3f37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41784 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

d)zhJC_-`oU{oMk% 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

V@)^dGu$bF^$RQJ!H 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 + >('/users/current') if (fetchData != null) { data = await fetchData(context, authentication.api) } diff --git a/utils/authentication/index.ts b/utils/authentication/index.ts index a37f023..291fe82 100644 --- a/utils/authentication/index.ts +++ b/utils/authentication/index.ts @@ -1,4 +1,4 @@ -import { UserCurrent } from 'models/User' +import { UserCurrent } from '../../models/User' export interface RefreshTokenResponse { accessToken: string