feat: add user profile page (#6)

This commit is contained in:
Walid
2022-01-14 23:15:51 +01:00
committed by GitHub
parent 9229131c1a
commit ee73885fe9
32 changed files with 698 additions and 193 deletions

View File

@ -1,188 +1,178 @@
import Image from 'next/image'
import { PencilIcon, PhotographIcon } from '@heroicons/react/solid'
import { useState } from 'react'
import classNames from 'classnames'
import useTranslation from 'next-translate/useTranslation'
import date from 'date-and-time'
import useTranslation from 'next-translate/useTranslation'
import { XIcon } from '@heroicons/react/solid'
import { API_URL } from '../../../tools/api'
import { UserPublic } from '../../../models/User'
import { UserProfileGuilds } from './UserProfileGuilds'
import { UserProfileGuild } from './UserProfileGuilds/UserProfileGuild'
export interface UserProfileProps {
className?: string
isOwner?: boolean
user: UserPublic
}
export const UserProfile: React.FC<UserProfileProps> = (props) => {
const { user, isOwner = false } = props
const { user } = props
const { t } = useTranslation()
const handleSubmitChanges = (
event: React.FormEvent<HTMLFormElement>
): void => {
event.preventDefault()
const [showPopup, setShowPopup] = useState<boolean>(false)
const [confirmation, setConfirmation] = useState<boolean>(false)
const handleConfirmationState = (): void => {
setConfirmation((confirmation) => !confirmation)
}
const handlePopupVisibility = (): void => {
setShowPopup((showPopup) => !showPopup)
}
return (
<div className='h-full flex flex-col items-center justify-center'>
<div className='min-w-[1080px]'>
<div className='flex justify-between items-center'>
<div className='w-max flex items-center'>
<div
className={classNames(
'relative flex justify-center items-center rounded-full overflow-hidden transition-all shadow-lg',
{
'after:absolute after:w-full after:h-full border-4 border-white cursor-pointer':
isOwner
}
)}
>
{isOwner && (
<div className='absolute w-full h-full z-50'>
<button className='relative w-full h-full flex items-center justify-center transition hover:-translate-y-1'>
<input
type='file'
className='absolute w-full h-full opacity-0 cursor-pointer'
/>
<PhotographIcon className='w-14 h-14' />
</button>
<div className='relative h-full flex flex-col items-center justify-center'>
<div
className={classNames('transition', {
'blur-3xl select-none': showPopup
})}
>
<div className='max-w-[1000px] px-12'>
<div className='flex justify-between items-center'>
<div className='w-max flex items-center'>
<div className='relative flex justify-center items-center rounded-full overflow-hidden transition-all shadow-lg'>
<Image
className='rounded-full'
src={
user.logo != null
? API_URL + user.logo
: '/images/data/user-default.png'
}
alt='Profil Picture'
draggable='false'
height={125}
width={125}
/>
</div>
<div className='flex flex-col ml-10'>
<div className='flex items-center mb-2'>
<p className='text-3xl font-bold space tracking-wide text-white'>
{user.name}
</p>
<p className='ml-8 text-sm tracking-widest text-white opacity-40 select-none'>
{date.format(new Date(user.createdAt), 'DD/MM/YYYY')}
</p>
</div>
)}
<Image
className={classNames('rounded-full', {
'opacity-30': isOwner
})}
src='/images/data/divlo.png'
alt={'Profil Picture'}
draggable='false'
height={125}
width={125}
/>
<div className='text-left my-2'>
{user.email != null && (
<p className='font-bold'>
Email:{' '}
<a
href={`mailto:${user.email}`}
target='_blank'
className='relative ml-2 opacity-80 hover:opacity-100 transition-all no-underline font-normal tracking-wide after:absolute after:left-0 after:bottom-[-1px] after:bg-black dark:after:bg-white after:h-[1px] after:w-0 after:transition-all hover:after:w-full'
rel='noreferrer'
>
{user.email}
</a>
</p>
)}
{user.website != null && (
<p className='font-bold'>
{t('application:website')}:{' '}
<a
href={user.website}
className='relative ml-2 opacity-80 hover:opacity-100 transition-all no-underline font-normal tracking-wide after:absolute after:left-0 after:bottom-[-2px] after:bg-black dark:after:bg-white after:h-[1px] after:w-0 after:transition-all hover:after:w-full'
>
{user.website}
</a>
</p>
)}
{user.status != null && (
<p className='flex font-bold'>
{t('application:status')}:{' '}
<span className='ml-2 font-normal tracking-wide'>
{user.status}
</span>
</p>
)}
</div>
</div>
</div>
<div className='flex flex-col ml-10'>
<div className='flex items-center mb-2 border'>
<form onSubmit={handleSubmitChanges}>
<input
type='text'
value={user.name}
className='min-w-[10px] border bg-transparent text-3xl font-bold space tracking-wide text-white'
disabled
/>
</form>
{isOwner && (
<button className='ml-2 text-gray-500'>
<PencilIcon className='w-6 h-6' />
</button>
)}
<span className='h-5 w-5 bg-error shadow-error ml-4 rounded-full'>
{''}
</span>
<p className='ml-8 text-sm tracking-widest text-white opacity-40 select-none'>
{date.format(new Date(user.createdAt), 'DD/MM/YYYY')}
</p>
</div>
<div className='text-left my-2'>
{user.website != null && (
<p className='font-bold'>
{t('application:website')}:{' '}
<a
href={user.website}
className='relative ml-2 opacity-80 hover:opacity-100 transition-all no-underline font-normal tracking-wide after:absolute after:left-0 after:bottom-[-1px] after:bg-black dark:after:bg-white after:h-[1px] after:w-0 after:transition-all hover:after:w-full'
>
{user.website}
</a>
</p>
)}
{user.email != null && (
<p className='font-bold'>
Email:{' '}
<a
href={`mailto:${user.email}`}
target='_blank'
className='relative ml-2 opacity-80 hover:opacity-100 transition-all no-underline font-normal tracking-wide after:absolute after:left-0 after:bottom-[-1px] after:bg-black dark:after:bg-white after:h-[1px] after:w-0 after:transition-all hover:after:w-full'
rel='noreferrer'
>
{user.email}
</a>
</p>
)}
</div>
<div className='py-8 px-4' onClick={handlePopupVisibility}>
<UserProfileGuilds
isPublicGuilds={user.settings.isPublicGuilds}
/>
</div>
</div>
<div className='flex -space-x-7'>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_1.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_2.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_3.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_4.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_5.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_6.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
{/* End of the guilds list */}
<div className='w-[60px] h-[60px] flex justify-center items-center rounded-full filter drop-shadow-lg bg-gray-300 dark:bg-gray-800 z-10'>
<span className='font-bold text-black dark:text-white text-xl select-none'>
+4
</span>
</div>
<div className='mt-7'>
{user.biography != null && (
<p className='w-[45%]'>{user.biography}</p>
)}
</div>
</div>
<div className='mt-7'>
{user.biography != null && (
<p className='w-[45%]'>{user.biography}</p>
</div>
<div
className={classNames(
'absolute flex justify-center items-center top-0 h-full w-full bg-zinc-900/75 transition opacity-0 pointer-events-none',
{
'opacity-100 visible pointer-events-auto': showPopup
}
)}
>
<div
className={classNames(
'relative h-[400px] w-[400px] py-2 rounded-2xl shadow-xl bg-gray-200 dark:bg-gray-800 scale-0 transition overflow-y-auto overflow-x-hidden',
{ 'scale-100': showPopup }
)}
>
<div
className={classNames('relative transition h-full', {
'-translate-x-[150%]': confirmation
})}
>
<UserProfileGuild
handleConfirmationState={handleConfirmationState}
/>
</div>
<div
className={classNames(
'absolute w-full h-full flex flex-col justify-center items-center transition-all top-0 left-[150%]',
{ 'left-[0%]': confirmation }
)}
>
<Image
src='/images/svg/design/join-guild.svg'
alt='Joing Guild Illustration'
height={150}
width={150}
/>
<div className='flex flex-col mt-8'>
<h1 className='text-xl mb-6 text-center'>Rejoindre la guild ?</h1>
<div className='flex gap-7'>
<button
className='px-8 py-2 rounded-3xl bg-success hover:opacity-50 transition'
onClick={handleConfirmationState}
>
Oui
</button>
<button
className='px-8 py-2 rounded-3xl bg-error hover:opacity-50 transition'
onClick={handleConfirmationState}
>
Non
</button>
</div>
</div>
</div>
</div>
<XIcon
height={40}
onClick={() => setShowPopup(false)}
className='absolute top-8 right-8 cursor-pointer hover:rotate-180 transition'
/>
</div>
</div>
)

View File

@ -0,0 +1,18 @@
import { Meta, Story } from '@storybook/react'
import {
UserProfileGuild as Component,
UserProfileGuildProps
} from './UserProfileGuild'
const Stories: Meta = {
title: 'UserProfileGuild',
component: Component
}
export default Stories
export const UserProfileGuild: Story<UserProfileGuildProps> = (arguments_) => {
return <Component {...arguments_} />
}
UserProfileGuild.args = { className: 'text-center' }

View File

@ -0,0 +1,12 @@
import { render } from '@testing-library/react'
import { UserProfileGuild } from './UserProfileGuild'
describe('<UserProfileGuild />', () => {
it('should render successfully', () => {
const { baseElement } = render(
<UserProfileGuild handleConfirmationState={() => {}} />
)
expect(baseElement).toBeTruthy()
})
})

View File

@ -0,0 +1,46 @@
import Image from 'next/image'
import { LoginIcon } from '@heroicons/react/solid'
export interface UserProfileGuildProps {
className?: string
handleConfirmationState: () => void
}
export const UserProfileGuild: React.FC<UserProfileGuildProps> = (props) => {
const { handleConfirmationState } = props
return (
<div
className='relative flex w-full cursor-pointer transition group'
onClick={handleConfirmationState}
>
<div className='relative group-hover:-translate-x-20 px-8 py-5 w-full transition'>
<div className='flex group-hover:opacity-40 transition'>
<div className='flex justify-center rounded-full filter drop-shadow-lg mr-8 min-w-[60px] min-h-[60px] select-none'>
<Image
className='rounded-full'
src='/images/guilds/Guild_1.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex flex-col'>
<h1 className='text-xl font-bold'>Guild Name</h1>
<p className='text-gray-300 mt-2'>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis,
nam.
</p>
</div>
</div>
<div className='absolute top-0 right-[-80px] flex justify-center items-center w-[80px] h-full'>
<LoginIcon
height={40}
className='fill-green-600 drop-shadow-[0_0_15px_rgba(22,163,74,0.50)]'
/>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1 @@
export * from './UserProfileGuild'

View File

@ -0,0 +1,20 @@
import { Meta, Story } from '@storybook/react'
import {
UserProfileGuilds as Component,
UserProfileGuildsProps
} from './UserProfileGuilds'
const Stories: Meta = {
title: 'UserProfileGuilds',
component: Component
}
export default Stories
export const UserProfileGuilds: Story<UserProfileGuildsProps> = (
arguments_
) => {
return <Component {...arguments_} />
}
UserProfileGuilds.args = {}

View File

@ -0,0 +1,10 @@
import { render } from '@testing-library/react'
import { UserProfileGuilds } from './UserProfileGuilds'
describe('<UserProfileGuilds />', () => {
it('should render successfully', () => {
const { baseElement } = render(<UserProfileGuilds />)
expect(baseElement).toBeTruthy()
})
})

View File

@ -0,0 +1,104 @@
import Image from 'next/image'
import classNames from 'classnames'
import { EyeOffIcon } from '@heroicons/react/solid'
import useTranslation from 'next-translate/useTranslation'
export interface UserProfileGuildsProps {
isPublicGuilds?: boolean
}
export const UserProfileGuilds: React.FC<UserProfileGuildsProps> = (props) => {
const { isPublicGuilds = false } = props
const { t } = useTranslation()
return (
<div
className={classNames('relative cursor-pointer', {
'cursor-auto': !isPublicGuilds
})}
>
<div
className={classNames('flex -space-x-7', {
'blur-lg select-none': !isPublicGuilds
})}
>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_1.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_2.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_3.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_4.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_5.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='flex justify-center items-center rounded-full filter drop-shadow-lg'>
<Image
className='rounded-full'
src='/images/guilds/Guild_6.svg'
alt={'Profil Picture'}
draggable='false'
height={60}
width={60}
/>
</div>
<div className='w-[60px] h-[60px] flex justify-center items-center rounded-full filter drop-shadow-lg bg-gray-300 dark:bg-gray-800 z-10'>
<span className='font-bold text-black dark:text-white text-xl select-none'>
+4
</span>
</div>
</div>
<div
className={classNames(
'absolute flex items-center top-1/2 -translate-y-1/2',
{ hidden: isPublicGuilds }
)}
>
<EyeOffIcon height={25} />
<p className='drop-shadow-2xl ml-4'>
{t('application:private-user-guilds-list')}
</p>
</div>
</div>
)
}

View File

@ -0,0 +1 @@
export * from './UserProfileGuilds'