2021-04-07 15:03:02 +00:00
import Head from 'next/head' ;
import Link from 'next/link' ;
2021-04-20 12:56:50 +00:00
import { useRef , useState } from 'react' ;
2021-04-29 12:36:37 +00:00
import { useRouter } from 'next/router' ;
2021-04-07 15:03:02 +00:00
import prisma from '../../lib/prisma' ;
2021-04-20 12:56:50 +00:00
import Modal from '../../components/Modal' ;
2021-04-07 15:03:02 +00:00
import Shell from '../../components/Shell' ;
import SettingsShell from '../../components/Settings' ;
2021-05-08 20:26:19 +00:00
import Avatar from '../../components/Avatar' ;
2021-04-07 15:03:02 +00:00
import { signIn , useSession , getSession } from 'next-auth/client' ;
2021-04-26 12:01:21 +00:00
import TimezoneSelect from 'react-timezone-select' ;
2021-06-09 12:26:00 +00:00
import { UsernameInput } from "../../components/ui/UsernameInput" ;
import ErrorAlert from "../../components/ui/alerts/Error" ;
2021-04-07 15:03:02 +00:00
export default function Settings ( props ) {
const [ session , loading ] = useSession ( ) ;
2021-04-29 12:36:37 +00:00
const router = useRouter ( ) ;
2021-04-20 12:56:50 +00:00
const [ successModalOpen , setSuccessModalOpen ] = useState ( false ) ;
2021-04-29 13:04:08 +00:00
const usernameRef = useRef < HTMLInputElement > ( ) ;
const nameRef = useRef < HTMLInputElement > ( ) ;
2021-04-29 13:47:01 +00:00
const descriptionRef = useRef < HTMLTextAreaElement > ( ) ;
2021-04-29 13:04:08 +00:00
const avatarRef = useRef < HTMLInputElement > ( ) ;
2021-04-26 12:01:21 +00:00
const [ selectedTimeZone , setSelectedTimeZone ] = useState ( { value : props.user.timeZone } ) ;
2021-05-17 22:10:40 +00:00
const [ selectedWeekStartDay , setSelectedWeekStartDay ] = useState ( props . user . weekStart || 'Sunday' ) ;
2021-04-07 15:03:02 +00:00
2021-06-09 12:26:00 +00:00
const [ hasErrors , setHasErrors ] = useState ( false ) ;
const [ errorMessage , setErrorMessage ] = useState ( '' ) ;
2021-04-07 15:03:02 +00:00
if ( loading ) {
return < p className = "text-gray-400" > Loading . . . < / p > ;
}
2021-04-20 12:56:50 +00:00
const closeSuccessModal = ( ) = > { setSuccessModalOpen ( false ) ; }
2021-06-09 12:26:00 +00:00
const handleError = async ( resp ) = > {
if ( ! resp . ok ) {
const error = await resp . json ( ) ;
throw new Error ( error . message ) ;
}
}
2021-04-07 15:03:02 +00:00
async function updateProfileHandler ( event ) {
event . preventDefault ( ) ;
const enteredUsername = usernameRef . current . value ;
const enteredName = nameRef . current . value ;
const enteredDescription = descriptionRef . current . value ;
2021-04-29 12:36:37 +00:00
const enteredAvatar = avatarRef . current . value ;
2021-04-26 12:01:21 +00:00
const enteredTimeZone = selectedTimeZone . value ;
2021-05-17 22:10:40 +00:00
const enteredWeekStartDay = selectedWeekStartDay ;
2021-04-07 15:03:02 +00:00
// TODO: Add validation
const response = await fetch ( '/api/user/profile' , {
method : 'PATCH' ,
2021-05-17 22:10:40 +00:00
body : JSON.stringify ( { username : enteredUsername , name : enteredName , description : enteredDescription , avatar : enteredAvatar , timeZone : enteredTimeZone , weekStart : enteredWeekStartDay } ) ,
2021-04-07 15:03:02 +00:00
headers : {
'Content-Type' : 'application/json'
}
2021-06-09 20:32:02 +00:00
} ) . then ( handleError ) . then ( ( ) = > {
setSuccessModalOpen ( true ) ;
setHasErrors ( false ) ; // dismiss any open errors
} ) . catch ( ( err ) = > {
2021-06-09 12:26:00 +00:00
setHasErrors ( true ) ;
setErrorMessage ( err . message ) ;
2021-04-07 15:03:02 +00:00
} ) ;
}
return (
2021-04-07 21:07:16 +00:00
< Shell heading = "Profile" >
2021-04-07 15:03:02 +00:00
< Head >
< title > Profile | Calendso < / title >
< link rel = "icon" href = "/favicon.ico" / >
< / Head >
< SettingsShell >
< form className = "divide-y divide-gray-200 lg:col-span-9" onSubmit = { updateProfileHandler } >
2021-06-09 12:26:00 +00:00
{ hasErrors && < ErrorAlert message = { errorMessage } / > }
2021-04-07 15:03:02 +00:00
< div className = "py-6 px-4 sm:p-6 lg:pb-8" >
< div >
< h2 className = "text-lg leading-6 font-medium text-gray-900" > Profile < / h2 >
< p className = "mt-1 text-sm text-gray-500" >
Review and change your public page details .
< / p >
< / div >
< div className = "mt-6 flex flex-col lg:flex-row" >
< div className = "flex-grow space-y-6" >
< div className = "flex" >
< div className = "w-1/2 mr-2" >
2021-06-09 12:26:00 +00:00
< UsernameInput ref = { usernameRef } defaultValue = { props . user . username } / >
2021-04-07 15:03:02 +00:00
< / div >
< div className = "w-1/2 ml-2" >
< label htmlFor = "name" className = "block text-sm font-medium text-gray-700" > Full name < / label >
2021-05-05 21:58:42 +00:00
< input ref = { nameRef } type = "text" name = "name" id = "name" autoComplete = "given-name" placeholder = "Your name" required className = "mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" defaultValue = { props . user . name } / >
2021-04-07 15:03:02 +00:00
< / div >
< / div >
< div >
< label htmlFor = "about" className = "block text-sm font-medium text-gray-700" >
About
< / label >
< div className = "mt-1" >
< textarea ref = { descriptionRef } id = "about" name = "about" placeholder = "A little something about yourself." rows = { 3 } className = "shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md" > { props . user . bio } < / textarea >
< / div >
< / div >
2021-04-16 02:09:22 +00:00
< div >
< label htmlFor = "timeZone" className = "block text-sm font-medium text-gray-700" >
Timezone
< / label >
< div className = "mt-1" >
2021-04-26 12:01:21 +00:00
< TimezoneSelect id = "timeZone" value = { selectedTimeZone } onChange = { setSelectedTimeZone } className = "shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md" / >
2021-04-16 02:09:22 +00:00
< / div >
< / div >
2021-05-17 22:10:40 +00:00
< div >
< label htmlFor = "weekStart" className = "block text-sm font-medium text-gray-700" >
First Day of Week
< / label >
< div className = "mt-1" >
< select id = "weekStart" value = { selectedWeekStartDay } onChange = { e = > setSelectedWeekStartDay ( e . target . value ) } className = "shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md" >
< option value = "Sunday" > Sunday < / option >
< option value = "Monday" > Monday < / option >
< / select >
< / div >
< / div >
2021-04-07 15:03:02 +00:00
< / div >
< div className = "mt-6 flex-grow lg:mt-0 lg:ml-6 lg:flex-grow-0 lg:flex-shrink-0" >
< p className = "mb-2 text-sm font-medium text-gray-700" aria - hidden = "true" >
Photo
< / p >
< div className = "mt-1 lg:hidden" >
< div className = "flex items-center" >
< div className = "flex-shrink-0 inline-block rounded-full overflow-hidden h-12 w-12" aria - hidden = "true" >
2021-05-08 20:26:19 +00:00
< Avatar user = { props . user } className = "rounded-full h-full w-full" / >
2021-04-07 15:03:02 +00:00
< / div >
2021-04-29 12:36:37 +00:00
{ / * < d i v c l a s s N a m e = " m l - 5 r o u n d e d - m d s h a d o w - s m " >
2021-04-07 15:03:02 +00:00
< div className = "group relative border border-gray-300 rounded-md py-2 px-3 flex items-center justify-center hover:bg-gray-50 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-blue-500" >
< label htmlFor = "user_photo" className = "relative text-sm leading-4 font-medium text-gray-700 pointer-events-none" >
< span > Change < / span >
< span className = "sr-only" > user photo < / span >
< / label >
< input id = "user_photo" name = "user_photo" type = "file" className = "absolute w-full h-full opacity-0 cursor-pointer border-gray-300 rounded-md" / >
< / div >
2021-04-29 12:36:37 +00:00
< / div > * / }
2021-04-07 15:03:02 +00:00
< / div >
< / div >
< div className = "hidden relative rounded-full overflow-hidden lg:block" >
2021-05-08 20:26:19 +00:00
< Avatar
user = { props . user }
className = "relative rounded-full w-40 h-40"
fallback = { < div className = "relative bg-blue-600 rounded-full w-40 h-40" > < / div > }
/ >
2021-04-29 12:36:37 +00:00
{ / * < l a b e l h t m l F o r = " u s e r - p h o t o " c l a s s N a m e = " a b s o l u t e i n s e t - 0 w - f u l l h - f u l l b g - b l a c k b g - o p a c i t y - 7 5 f l e x i t e m s - c e n t e r j u s t i f y - c e n t e r t e x t - s m f o n t - m e d i u m t e x t - w h i t e o p a c i t y - 0 h o v e r : o p a c i t y - 1 0 0 f o c u s - w i t h i n : o p a c i t y - 1 0 0 " >
2021-04-07 15:03:02 +00:00
< span > Change < / span >
< span className = "sr-only" > user photo < / span >
< input type = "file" id = "user-photo" name = "user-photo" className = "absolute inset-0 w-full h-full opacity-0 cursor-pointer border-gray-300 rounded-md" / >
2021-04-29 12:36:37 +00:00
< / label > * / }
< / div >
< div className = "mt-4" >
< label htmlFor = "avatar" className = "block text-sm font-medium text-gray-700" > Avatar URL < / label >
< input ref = { avatarRef } type = "text" name = "avatar" id = "avatar" placeholder = "URL" className = "mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" defaultValue = { props . user . avatar } / >
2021-04-07 15:03:02 +00:00
< / div >
< / div >
< / div >
< hr className = "mt-8" / >
< div className = "py-4 flex justify-end" >
< button type = "submit" className = "ml-2 bg-blue-600 border border-transparent rounded-md shadow-sm py-2 px-4 inline-flex justify-center text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" >
Save
< / button >
< / div >
< / div >
< / form >
2021-04-20 12:56:50 +00:00
< Modal heading = "Profile updated successfully" description = "Your user profile has been updated successfully." open = { successModalOpen } handleClose = { closeSuccessModal } / >
2021-04-07 15:03:02 +00:00
< / SettingsShell >
< / Shell >
) ;
}
export async function getServerSideProps ( context ) {
const session = await getSession ( context ) ;
2021-05-07 16:01:29 +00:00
if ( ! session ) {
return { redirect : { permanent : false , destination : '/auth/login' } } ;
}
2021-04-07 15:03:02 +00:00
const user = await prisma . user . findFirst ( {
where : {
email : session.user.email ,
} ,
select : {
id : true ,
username : true ,
name : true ,
email : true ,
bio : true ,
2021-04-16 02:09:22 +00:00
avatar : true ,
timeZone : true ,
2021-05-17 22:10:40 +00:00
weekStart : true ,
2021-04-07 15:03:02 +00:00
}
} ) ;
return {
props : { user } , // will be passed to the page component as props
}
2021-05-04 11:36:06 +00:00
}