fix: improve overall rendering v3 (#25)
Follow-up of #15 Co-authored-by: Walidoux <ma.walidkorchi@icloud.com>
This commit is contained in:
parent
d2b0e8831c
commit
a64325f5b8
@ -1,15 +1,15 @@
|
||||
FROM node:16.14.0 AS dependencies
|
||||
FROM node:16.14.2 AS dependencies
|
||||
WORKDIR /usr/src/app
|
||||
COPY ./package*.json ./
|
||||
RUN npm install
|
||||
|
||||
FROM node:16.14.0 AS builder
|
||||
FROM node:16.14.2 AS builder
|
||||
WORKDIR /usr/src/app
|
||||
COPY ./ ./
|
||||
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
|
||||
RUN npm run build
|
||||
|
||||
FROM node:16.14.0 AS runner
|
||||
FROM node:16.14.2 AS runner
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production
|
||||
COPY --from=builder /usr/src/app/next.config.js ./next.config.js
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
Thream's website to stay close with your friends and communities.
|
||||
|
||||
It uses [Thream/api](https://github.com/Thream/api) v1.0.0.
|
||||
It uses [Thream/api](https://github.com/Thream/api) [v1.0.0](https://github.com/Thream/api/releases/tag/v1.0.0).
|
||||
|
||||
## ⚙️ Getting Started
|
||||
|
||||
|
@ -184,6 +184,7 @@ export const Application: React.FC<ApplicationProps> = (props) => {
|
||||
title='Settings'
|
||||
>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full'
|
||||
src={
|
||||
user.logo == null
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import { channelExample } from '../../../../cypress/fixtures/channels/channel'
|
||||
import { Channel } from './Channel'
|
||||
|
||||
describe('<Channel />', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(
|
||||
<Channel channel={channelExample} path={{ channelId: 1, guildId: 1 }} />
|
||||
)
|
||||
expect(baseElement).toBeTruthy()
|
||||
})
|
||||
})
|
@ -1,8 +1,12 @@
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { CogIcon } from '@heroicons/react/solid'
|
||||
|
||||
import { GuildsChannelsPath } from '../../Application'
|
||||
import { Channel as ChannelType } from '../../../../models/Channel'
|
||||
import { useGuildMember } from '../../../../contexts/GuildMember'
|
||||
import { IconButton } from '../../../design/IconButton'
|
||||
|
||||
export interface ChannelProps {
|
||||
path: GuildsChannelsPath
|
||||
@ -12,20 +16,39 @@ export interface ChannelProps {
|
||||
|
||||
export const Channel: React.FC<ChannelProps> = (props) => {
|
||||
const { channel, path, selected = false } = props
|
||||
const router = useRouter()
|
||||
|
||||
const { member } = useGuildMember()
|
||||
|
||||
return (
|
||||
<Link href={`/application/${path.guildId}/${channel.id}`}>
|
||||
<a
|
||||
className={classNames(
|
||||
'group my-3 mx-3 flex items-center justify-between rounded-lg py-2 text-sm transition-colors duration-200 hover:bg-gray-100 dark:hover:bg-gray-600',
|
||||
'group relative my-3 mx-3 flex items-center justify-between overflow-hidden rounded-lg py-2 text-sm transition-all duration-200 hover:bg-gray-100 dark:hover:bg-gray-600',
|
||||
{
|
||||
'font-semibold text-green-800 dark:text-green-400': selected
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span className='ml-2 mr-4' data-cy='channel-name'>
|
||||
<span
|
||||
className='max-[315px] ml-2 mr-4 break-all'
|
||||
data-cy='channel-name'
|
||||
>
|
||||
# {channel.name}
|
||||
</span>
|
||||
{member.isOwner && (
|
||||
<IconButton
|
||||
onClick={async () => {
|
||||
await router.push(
|
||||
`/application/${channel.guildId}/${channel.id}/settings`
|
||||
)
|
||||
}}
|
||||
className='bg-unherit absolute -right-10 h-full w-8 transition-all group-hover:right-0 group-hover:shadow-lg dark:group-hover:bg-gray-600'
|
||||
title='Settings'
|
||||
>
|
||||
<CogIcon height={20} width={20} />
|
||||
</IconButton>
|
||||
)}
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
|
@ -38,6 +38,7 @@ export const ConfirmGuildJoin: React.FC<ConfirmGuildJoinProps> = ({
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
quality={100}
|
||||
src='/images/svg/design/join-guild.svg'
|
||||
alt='Join Guild Illustration'
|
||||
height={150}
|
||||
|
@ -50,7 +50,7 @@ export const GuildSettings: React.FC = () => {
|
||||
setInputValues(formData as any)
|
||||
return {
|
||||
type: 'success',
|
||||
value: 'common:name'
|
||||
value: 'application:saved-information'
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
@ -130,6 +130,7 @@ export const GuildSettings: React.FC = () => {
|
||||
</div>
|
||||
<div className='flex items-center justify-center rounded-full bg-black shadow-xl'>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full opacity-50'
|
||||
src={
|
||||
guild.icon == null
|
||||
@ -193,7 +194,10 @@ export const GuildSettings: React.FC = () => {
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<FormState state={fetchState} message={message} />
|
||||
<FormState
|
||||
state={fetchState}
|
||||
message={getErrorTranslation(errors.description) ?? message}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
|
@ -21,6 +21,7 @@ export const Guild: React.FC<GuildProps> = (props) => {
|
||||
>
|
||||
<div className='pl-[6px]'>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full'
|
||||
src={
|
||||
guild.icon != null
|
||||
|
@ -61,6 +61,7 @@ export const GuildPublic: React.FC<GuildPublicProps> = (props) => {
|
||||
onClick={handleIsConfirmed}
|
||||
>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full'
|
||||
src={
|
||||
guild.icon != null
|
||||
|
@ -33,9 +33,7 @@ export const Member: React.FC<MemberProps> = (props) => {
|
||||
<p data-cy='member-user-name' className='truncate font-bold'>
|
||||
{member.user.name}
|
||||
</p>
|
||||
{member.user.status != null && (
|
||||
<span className='block w-44 truncate'>{member.user.status}</span>
|
||||
)}
|
||||
{member.user.status != null && member.user.status}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -25,6 +25,7 @@ export const Message: React.FC<MessageProps> = (props) => {
|
||||
<div className='mr-4 flex h-12 w-12 flex-shrink-0 items-center justify-center'>
|
||||
<div className='h-10 w-10 drop-shadow-md'>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full'
|
||||
src={
|
||||
message.member.user.logo == null
|
||||
|
@ -23,6 +23,7 @@ export const PopupGuild: React.FC<PopupGuildProps> = (props) => {
|
||||
<PopupGuildCard
|
||||
image={
|
||||
<Image
|
||||
quality={100}
|
||||
src='/images/svg/design/create-guild.svg'
|
||||
alt={t('application:create-a-guild')}
|
||||
draggable='false'
|
||||
@ -40,6 +41,7 @@ export const PopupGuild: React.FC<PopupGuildProps> = (props) => {
|
||||
<PopupGuildCard
|
||||
image={
|
||||
<Image
|
||||
quality={100}
|
||||
src='/images/svg/design/join-guild.svg'
|
||||
alt={t('application:join-a-guild')}
|
||||
draggable='false'
|
||||
|
@ -10,6 +10,7 @@ describe('<PopupGuildCard />', () => {
|
||||
<PopupGuildCard
|
||||
image={
|
||||
<Image
|
||||
quality={100}
|
||||
src='/images/svg/design/create-server.svg'
|
||||
alt=''
|
||||
width={230}
|
||||
|
@ -104,7 +104,6 @@ export const SendMessage: React.FC<SendMessageProps> = (props) => {
|
||||
onChange={handleTextareaChange}
|
||||
value={message}
|
||||
ref={textareaReference}
|
||||
autoFocus
|
||||
/>
|
||||
</form>
|
||||
<div className='flex h-full items-center justify-around pr-6'>
|
||||
|
@ -24,6 +24,7 @@ export const UserProfile: React.FC<UserProfileProps> = (props) => {
|
||||
<div className='flex w-max items-center'>
|
||||
<div className='relative flex items-center justify-center overflow-hidden rounded-full shadow-lg transition-all'>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full'
|
||||
src={
|
||||
user.logo != null
|
||||
|
@ -2,9 +2,10 @@ import Image from 'next/image'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import { useState } from 'react'
|
||||
import { Form } from 'react-component-form'
|
||||
import { PhotographIcon } from '@heroicons/react/solid'
|
||||
import { EyeIcon, PhotographIcon } from '@heroicons/react/solid'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import axios from 'axios'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { API_URL } from '../../../tools/api'
|
||||
import { Input } from '../../design/Input'
|
||||
@ -17,7 +18,7 @@ import { useAuthentication } from '../../../tools/authentication'
|
||||
import { Button } from '../../design/Button'
|
||||
import { FormState } from '../../design/FormState'
|
||||
import { useForm, HandleSubmitCallback } from '../../../hooks/useForm'
|
||||
import { userCurrentSchema, userSchema } from '../../../models/User'
|
||||
import { userSchema } from '../../../models/User'
|
||||
import { userSettingsSchema } from '../../../models/UserSettings'
|
||||
|
||||
export const UserSettings: React.FC = () => {
|
||||
@ -45,7 +46,7 @@ export const UserSettings: React.FC = () => {
|
||||
validateSchema: {
|
||||
name: userSchema.name,
|
||||
status: Type.Optional(userSchema.status),
|
||||
email: Type.Optional(userCurrentSchema.email),
|
||||
email: Type.Optional(Type.Union([userSchema.email, Type.Null()])),
|
||||
website: Type.Optional(userSchema.website),
|
||||
biography: Type.Optional(userSchema.biography),
|
||||
isPublicGuilds: userSettingsSchema.isPublicGuilds,
|
||||
@ -63,6 +64,14 @@ export const UserSettings: React.FC = () => {
|
||||
`/users/current?redirectURI=${window.location.origin}/authentication/signin`,
|
||||
userData
|
||||
)
|
||||
setInputValues(formData as any)
|
||||
const hasEmailChanged = user.email !== userCurrentData.user.email
|
||||
if (hasEmailChanged) {
|
||||
return {
|
||||
type: 'success',
|
||||
value: 'application:success-email-changed'
|
||||
}
|
||||
}
|
||||
const { data: userCurrentSettings } = await authentication.api.put(
|
||||
'/users/current/settings',
|
||||
userSettings
|
||||
@ -74,10 +83,9 @@ export const UserSettings: React.FC = () => {
|
||||
settings: userCurrentSettings.settings
|
||||
}
|
||||
})
|
||||
setInputValues(formData as any)
|
||||
return {
|
||||
type: 'success',
|
||||
value: 'common:name'
|
||||
value: 'application:saved-information'
|
||||
}
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 400) {
|
||||
@ -185,6 +193,7 @@ export const UserSettings: React.FC = () => {
|
||||
</div>
|
||||
<div className='flex items-center justify-center rounded-full bg-black shadow-xl'>
|
||||
<Image
|
||||
quality={100}
|
||||
className='rounded-full opacity-50'
|
||||
src={
|
||||
user.logo != null
|
||||
@ -255,6 +264,24 @@ export const UserSettings: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className='flex h-full w-4/5 flex-col items-center justify-between pr-0 sm:w-[415px] lg:pl-12'>
|
||||
<div className='flex w-full items-center pt-14'>
|
||||
<Language className='!top-12' />
|
||||
<div className='ml-auto flex'>
|
||||
<SwitchTheme />
|
||||
<Link href={`/application/users/${user.id}`}>
|
||||
<a
|
||||
className='group ml-3 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-slate-200 transition-colors hover:bg-slate-300 dark:bg-slate-700 hover:dark:bg-slate-800'
|
||||
title='Preview Public Profile'
|
||||
>
|
||||
<EyeIcon
|
||||
height={20}
|
||||
className='opacity-50 transition-opacity group-hover:opacity-100'
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-14 flex w-full flex-col gap-4'>
|
||||
<SocialMediaButton
|
||||
socialMedia='Google'
|
||||
@ -269,10 +296,6 @@ export const UserSettings: React.FC = () => {
|
||||
className='w-full justify-center'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex w-full justify-between pt-14'>
|
||||
<Language />
|
||||
<SwitchTheme />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -12,6 +12,7 @@ export const Header: React.FC = () => {
|
||||
<a>
|
||||
<div className='flex items-center justify-center'>
|
||||
<Image
|
||||
quality={100}
|
||||
width={60}
|
||||
height={60}
|
||||
src='/images/icons/Thream.png'
|
||||
|
@ -7,7 +7,12 @@ import { Arrow } from './Arrow'
|
||||
import { LanguageFlag } from './LanguageFlag'
|
||||
import i18n from '../../../i18n.json'
|
||||
|
||||
export const Language: React.FC = () => {
|
||||
export interface LanguageProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const Language: React.FC<LanguageProps> = (props) => {
|
||||
const { className } = props
|
||||
const { lang: currentLanguage } = useTranslation()
|
||||
const [hiddenMenu, setHiddenMenu] = useState(true)
|
||||
|
||||
@ -46,7 +51,8 @@ export const Language: React.FC = () => {
|
||||
<ul
|
||||
data-cy='languages-list'
|
||||
className={classNames(
|
||||
'absolute -bottom-16 z-10 mt-3 mr-4 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-light dark:bg-black dark:shadow-dark',
|
||||
className,
|
||||
'absolute top-16 z-10 mr-4 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
|
||||
{ hidden: hiddenMenu }
|
||||
)}
|
||||
>
|
@ -10,6 +10,7 @@ export const LanguageFlag: React.FC<LanguageFlagProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
quality={100}
|
||||
width={35}
|
||||
height={35}
|
||||
src={`/images/svg/languages/${language}.svg`}
|
||||
|
1
components/Header/Language/index.ts
Normal file
1
components/Header/Language/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './Language'
|
@ -4,14 +4,14 @@ import useTranslation from 'next-translate/useTranslation'
|
||||
import { FetchState as FormStateType } from '../../../hooks/useFetchState'
|
||||
import { Loader } from '../Loader'
|
||||
|
||||
export interface FormStateProps {
|
||||
export interface FormStateProps extends React.ComponentPropsWithoutRef<'div'> {
|
||||
state: FormStateType
|
||||
message?: string | null
|
||||
id?: string
|
||||
}
|
||||
|
||||
export const FormState: React.FC<FormStateProps> = (props) => {
|
||||
const { state, message, id } = props
|
||||
const { state, message, id, ...rest } = props
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (state === 'loading') {
|
||||
@ -29,25 +29,26 @@ export const FormState: React.FC<FormStateProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
{...rest}
|
||||
className={classNames(
|
||||
'relative mt-6 flex max-w-xl flex-row items-center text-center font-medium',
|
||||
props.className,
|
||||
'mt-6 flex max-w-xl items-center text-center font-medium',
|
||||
{
|
||||
'text-red-800 dark:text-red-400': state === 'error',
|
||||
'text-green-800 dark:text-green-400': state === 'success'
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className='thumbnail absolute top-0 inline-block bg-cover font-headline'></div>
|
||||
<span id={id} className={classNames({ 'pl-6': state === 'error' })}>
|
||||
<div className='thumbnail inline bg-cover font-headline' />
|
||||
<span id={id} className='pl-2'>
|
||||
<b>{t(`errors:${state}`)}:</b> {message}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
.thumbnail {
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
height: ${state === 'error' ? '20px' : '25px'};
|
||||
background-image: url('/images/svg/icons/input/${state}.svg');
|
||||
}
|
||||
`}</style>
|
||||
|
@ -2,7 +2,9 @@ import { useMemo } from 'react'
|
||||
import Image from 'next/image'
|
||||
import classNames from 'classnames'
|
||||
|
||||
export type SocialMedia = 'Discord' | 'GitHub' | 'Google'
|
||||
import { ProviderOAuth } from '../../../models/OAuth'
|
||||
|
||||
export type SocialMedia = ProviderOAuth
|
||||
|
||||
type SocialMediaColors = {
|
||||
[key in SocialMedia]: string
|
||||
@ -27,6 +29,7 @@ const SocialMediaChildren: React.FC<SocialMediaChildrenProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
quality={100}
|
||||
width={20}
|
||||
height={20}
|
||||
src={`/images/svg/web/${socialMedia}.svg`}
|
||||
|
@ -86,6 +86,8 @@ export const useForm = (options: UseFormOptions): UseFormResult => {
|
||||
|
||||
const handleSubmit: HandleSubmit = (callback) => {
|
||||
return async (formData: ObjectAny, formElement) => {
|
||||
setErrors({})
|
||||
setMessageTranslationKey(undefined)
|
||||
if (replaceEmptyStringToNull) {
|
||||
formData = replaceEmptyStringInObjectToNull(
|
||||
formData,
|
||||
|
20
i18n.json
20
i18n.json
@ -3,21 +3,21 @@
|
||||
"defaultLocale": "en",
|
||||
"pages": {
|
||||
"*": ["common"],
|
||||
"/": ["home"],
|
||||
"/404": ["errors"],
|
||||
"/500": ["errors"],
|
||||
"/authentication/forgot-password": ["authentication", "errors"],
|
||||
"/authentication/reset-password": ["authentication", "errors"],
|
||||
"/authentication/signup": ["authentication", "errors"],
|
||||
"/authentication/signin": ["authentication", "errors"],
|
||||
"/application/users/[userId]": ["application", "errors"],
|
||||
"/application/users/settings": ["application", "authentication", "errors"],
|
||||
"/application/guilds/create": ["application", "errors"],
|
||||
"/application/guilds/join": ["application", "errors"],
|
||||
"/": ["home"],
|
||||
"/application": ["application", "errors"],
|
||||
"/application/[guildId]/settings": ["application", "errors"],
|
||||
"/application/[guildId]/[channelId]": ["application", "errors"],
|
||||
"/application/[guildId]/[channelId]/settings": ["application", "errors"],
|
||||
"/application/[guildId]/channels/create": ["application", "errors"]
|
||||
"/application/[guildId]/channels/create": ["application", "errors"],
|
||||
"/application/guilds/create": ["application", "errors"],
|
||||
"/application/guilds/join": ["application", "errors"],
|
||||
"/application/users/[userId]": ["application", "errors"],
|
||||
"/application/users/settings": ["application", "authentication", "errors"],
|
||||
"/authentication/forgot-password": ["authentication", "errors"],
|
||||
"/authentication/reset-password": ["authentication", "errors"],
|
||||
"/authentication/signin": ["authentication", "errors"],
|
||||
"/authentication/signup": ["authentication", "errors"]
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
"label-checkbox-guilds": "Show the list of guilds to everyone.",
|
||||
"nothing-here": "Nothing's here...",
|
||||
"save": "Save",
|
||||
"saved-information": "The information has been saved.",
|
||||
"success-email-changed": "Please check your emails to confirm your new email address. You are now signed out.",
|
||||
"delete": "Delete",
|
||||
"signout": "Sign out",
|
||||
"leave": "Leave",
|
||||
|
@ -15,6 +15,8 @@
|
||||
"label-checkbox-guilds": "Afficher la liste des guildes au public.",
|
||||
"nothing-here": "Il n'y a rien ici...",
|
||||
"save": "Sauvegarder",
|
||||
"saved-information": "Les informations ont été enregistrées.",
|
||||
"success-email-changed": "Veuillez vérifier vos emails pour confirmer votre nouvelle adresse email. Vous êtes maintenant déconnecté.",
|
||||
"delete": "Supprimer",
|
||||
"signout": "Se déconnecter",
|
||||
"leave": "Quitter",
|
||||
|
@ -2,8 +2,8 @@ import { Type } from '@sinclair/typebox'
|
||||
|
||||
import { date, id } from './utils'
|
||||
|
||||
export const providers = ['google', 'github', 'discord'] as const
|
||||
export const strategies = [...providers, 'local'] as const
|
||||
export const providers = ['Google', 'GitHub', 'Discord'] as const
|
||||
export const strategies = [...providers, 'Local'] as const
|
||||
|
||||
export const strategiesTypebox = strategies.map((strategy) =>
|
||||
Type.Literal(strategy)
|
||||
|
@ -25,7 +25,7 @@ export const userSchema = {
|
||||
Type.String({
|
||||
minLength: 1,
|
||||
maxLength: 255,
|
||||
format: 'uri-reference'
|
||||
format: 'uri'
|
||||
}),
|
||||
Type.Null()
|
||||
]),
|
||||
|
@ -8,6 +8,7 @@ module.exports = nextTranslate(
|
||||
images: {
|
||||
domains: [
|
||||
'api.thream.divlo.fr',
|
||||
'thream-api.herokuapp.com',
|
||||
...(process.env.NODE_ENV !== 'production' ? ['localhost'] : [])
|
||||
]
|
||||
},
|
||||
|
4482
package-lock.json
generated
4482
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
@ -32,29 +32,29 @@
|
||||
"postinstall": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/montserrat": "4.5.5",
|
||||
"@fontsource/roboto": "4.5.3",
|
||||
"@fontsource/montserrat": "4.5.7",
|
||||
"@fontsource/roboto": "4.5.5",
|
||||
"@heroicons/react": "1.0.6",
|
||||
"@sinclair/typebox": "0.23.4",
|
||||
"ajv": "8.10.0",
|
||||
"ajv": "8.11.0",
|
||||
"ajv-formats": "2.1.1",
|
||||
"axios": "0.26.1",
|
||||
"classnames": "2.3.1",
|
||||
"date-and-time": "2.3.0",
|
||||
"emoji-mart": "3.0.1",
|
||||
"katex": "0.15.3",
|
||||
"next": "12.1.0",
|
||||
"next-pwa": "5.4.6",
|
||||
"next": "12.1.4",
|
||||
"next-pwa": "5.4.7",
|
||||
"next-themes": "0.1.1",
|
||||
"next-translate": "1.3.5",
|
||||
"pretty-bytes": "6.0.0",
|
||||
"react": "17.0.2",
|
||||
"react-component-form": "2.0.0",
|
||||
"react-component-form": "3.0.1",
|
||||
"react-dom": "17.0.2",
|
||||
"react-infinite-scroll-component": "6.1.0",
|
||||
"react-markdown": "8.0.1",
|
||||
"react-markdown": "8.0.2",
|
||||
"react-responsive": "8.2.0",
|
||||
"react-swipeable": "6.2.0",
|
||||
"react-swipeable": "6.2.1",
|
||||
"react-textarea-autosize": "8.3.3",
|
||||
"read-pkg": "7.1.0",
|
||||
"rehype-katex": "6.0.2",
|
||||
@ -72,45 +72,45 @@
|
||||
"@commitlint/config-conventional": "16.2.1",
|
||||
"@lhci/cli": "0.9.0",
|
||||
"@saithodev/semantic-release-backmerge": "2.1.2",
|
||||
"@testing-library/jest-dom": "5.16.2",
|
||||
"@testing-library/jest-dom": "5.16.4",
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@types/emoji-mart": "3.0.9",
|
||||
"@types/hast": "2.3.4",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/katex": "0.11.1",
|
||||
"@types/node": "17.0.21",
|
||||
"@types/react": "17.0.40",
|
||||
"@types/katex": "0.14.0",
|
||||
"@types/node": "17.0.23",
|
||||
"@types/react": "17.0.43",
|
||||
"@types/react-responsive": "8.0.5",
|
||||
"@types/unist": "2.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.15.0",
|
||||
"@typescript-eslint/parser": "5.15.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||
"@typescript-eslint/parser": "5.18.0",
|
||||
"autoprefixer": "10.4.4",
|
||||
"cypress": "9.5.2",
|
||||
"cypress": "9.5.3",
|
||||
"editorconfig-checker": "4.0.2",
|
||||
"eslint": "8.11.0",
|
||||
"eslint-config-conventions": "1.1.1",
|
||||
"eslint-config-next": "12.1.0",
|
||||
"eslint": "8.12.0",
|
||||
"eslint-config-conventions": "2.0.0",
|
||||
"eslint-config-next": "12.1.4",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
"eslint-plugin-promise": "6.0.0",
|
||||
"eslint-plugin-unicorn": "41.0.0",
|
||||
"html-w3c-validator": "1.1.0",
|
||||
"eslint-plugin-unicorn": "42.0.0",
|
||||
"html-w3c-validator": "1.2.0",
|
||||
"husky": "7.0.4",
|
||||
"jest": "27.5.1",
|
||||
"lint-staged": "12.3.6",
|
||||
"lint-staged": "12.3.7",
|
||||
"markdownlint-cli": "0.31.1",
|
||||
"mockttp": "2.7.0",
|
||||
"next-secure-headers": "2.2.0",
|
||||
"plop": "3.0.5",
|
||||
"postcss": "8.4.12",
|
||||
"prettier": "2.6.0",
|
||||
"prettier": "2.6.2",
|
||||
"prettier-plugin-tailwindcss": "0.1.8",
|
||||
"semantic-release": "19.0.2",
|
||||
"serve": "13.0.2",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"tailwindcss": "3.0.23",
|
||||
"typescript": "4.6.2",
|
||||
"vercel": "24.0.0"
|
||||
"typescript": "4.6.3",
|
||||
"vercel": "24.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,14 @@ const Application = ({ Component, pageProps }: AppProps): JSX.Element => {
|
||||
|
||||
useEffect(() => {
|
||||
cookies.set('NEXT_LOCALE', lang)
|
||||
const appHeight = (): void => {
|
||||
document.documentElement.style.setProperty(
|
||||
'--app-height',
|
||||
`${window.innerHeight}px`
|
||||
)
|
||||
}
|
||||
window.addEventListener('resize', appHeight)
|
||||
appHeight()
|
||||
}, [lang])
|
||||
|
||||
return (
|
||||
|
@ -26,6 +26,7 @@ const Home: NextPage<FooterProps> = (props) => {
|
||||
<Link href='/authentication/signup'>
|
||||
<a>
|
||||
<Image
|
||||
quality={100}
|
||||
width={351}
|
||||
height={341}
|
||||
src='/images/svg/design/home.svg'
|
||||
|
@ -1,7 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
|
||||
<g fill="none" fill-rule="evenodd" transform="matrix(1 0 0 -1 0 22)">
|
||||
<path fill="#F95555" fill-rule="nonzero" d="M11 0C4.925 0 0 4.925 0 11s4.925 11 11 11 11-4.925 11-11S17.075 0 11 0z"/>
|
||||
<path fill="#FFF" fill-rule="nonzero" stroke="#FFF" stroke-width=".5" d="M9.875 7.063c0 .62.504 1.125 1.125 1.125s1.125-.504 1.125-1.126c0-.62-.504-1.125-1.125-1.125s-1.125.504-1.125 1.125z"/>
|
||||
<rect width="2.25" height="6.75" x="9.875" y="9.875" fill="#FFF" rx="1"/>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 22 22">
|
||||
<g fill="none" fill-rule="evenodd" transform="matrix(1 0 0 -1 0 22)">
|
||||
<path fill="#F95555" fill-rule="nonzero" d="M11 0C4.925 0 0 4.925 0 11s4.925 11 11 11 11-4.925 11-11S17.075 0 11 0z" />
|
||||
<path fill="#FFF" fill-rule="nonzero" stroke="#FFF" stroke-width=".5" d="M9.875 7.063c0 .62.504 1.125 1.125 1.125s1.125-.504 1.125-1.126c0-.62-.504-1.125-1.125-1.125s-1.125.504-1.125 1.125z" />
|
||||
<rect width="2.25" height="6.75" x="9.875" y="9.875" fill="#FFF" rx="1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 571 B |
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" ?><svg style="enable-background:new 0 0 612 792;" version="1.1" viewBox="0 0 612 792" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style type="text/css">
|
||||
.st0{fill:#41AD49;}
|
||||
</style><g><path class="st0" d="M562,396c0-141.4-114.6-256-256-256S50,254.6,50,396s114.6,256,256,256S562,537.4,562,396L562,396z M501.7,296.3l-241,241l0,0l-17.2,17.2L110.3,421.3l58.8-58.8l74.5,74.5l199.4-199.4L501.7,296.3L501.7,296.3z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 36 36">
|
||||
<path fill="#41ad49" d="M18 2a16 16 0 1 0 16 16A16 16 0 0 0 18 2Zm10.45 10.63L15.31 25.76L7.55 18a1.4 1.4 0 0 1 2-2l5.78 5.78l11.14-11.13a1.4 1.4 0 1 1 2 2Z" class="clr-i-solid clr-i-solid-path-1" />
|
||||
<path fill="none" d="M0 0h36v36H0z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 443 B |
@ -2,6 +2,12 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--app-height: 100vh;
|
||||
--scroll-bar-color: #c5c5c5;
|
||||
--scroll-bar-bg-color: transparent;
|
||||
}
|
||||
|
||||
#__next {
|
||||
@apply flex h-screen flex-col;
|
||||
}
|
||||
@ -12,12 +18,7 @@ body {
|
||||
}
|
||||
|
||||
.h-full-without-header {
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--scroll-bar-color: #c5c5c5;
|
||||
--scroll-bar-bg-color: transparent;
|
||||
height: calc(var(--app-height) - 64px);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
@ -17,6 +17,8 @@ module.exports = {
|
||||
boxShadow: {
|
||||
dark: '0px 1px 10px hsla(0, 0%, 100%, 0.2)',
|
||||
light: '0px 1px 10px rgba(0, 0, 0, 0.25)',
|
||||
darkFlag: '0px 1px 10px hsla(0, 0%, 100%, 0.2)',
|
||||
lightFlag: '0px 1px 10px rgba(0, 0, 0, 0.25)',
|
||||
green: '0 0 0 2px #27b05e',
|
||||
success: '0 2px 8px rgba(70, 200, 92, 0.3)',
|
||||
error: '0 2px 8px rgba(200, 69, 69, 0.5)'
|
||||
|
Reference in New Issue
Block a user