Prevent users from entering mixed case usernames
Booking pages are case insensitive new, so no more case sensitive usernames.
This commit is contained in:
		
							parent
							
								
									afa2e19f03
								
							
						
					
					
						commit
						1668785678
					
				
					 3 changed files with 496 additions and 318 deletions
				
			
		| 
						 | 
					@ -1,152 +1,268 @@
 | 
				
			||||||
import Link from 'next/link';
 | 
					import Link from "next/link";
 | 
				
			||||||
import {useEffect, useState} from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import {useRouter} from "next/router";
 | 
					import { useRouter } from "next/router";
 | 
				
			||||||
import {signOut, useSession} from 'next-auth/client';
 | 
					import { signOut, useSession } from "next-auth/client";
 | 
				
			||||||
import {MenuIcon, XIcon} from '@heroicons/react/outline';
 | 
					import { MenuIcon, XIcon } from "@heroicons/react/outline";
 | 
				
			||||||
import {collectPageParameters, telemetryEventTypes, useTelemetry} from "../lib/telemetry";
 | 
					import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../lib/telemetry";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Shell(props) {
 | 
					export default function Shell(props) {
 | 
				
			||||||
    const router = useRouter();
 | 
					  const router = useRouter();
 | 
				
			||||||
    const [ session, loading ] = useSession();
 | 
					  const [session, loading] = useSession();
 | 
				
			||||||
    const [ profileDropdownExpanded, setProfileDropdownExpanded ] = useState(false);
 | 
					  const [profileDropdownExpanded, setProfileDropdownExpanded] = useState(false);
 | 
				
			||||||
    const [ mobileMenuExpanded, setMobileMenuExpanded ] = useState(false);
 | 
					  const [mobileMenuExpanded, setMobileMenuExpanded] = useState(false);
 | 
				
			||||||
    let telemetry = useTelemetry();
 | 
					  const telemetry = useTelemetry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
        telemetry.withJitsu((jitsu) => {
 | 
					    telemetry.withJitsu((jitsu) => {
 | 
				
			||||||
            return jitsu.track(telemetryEventTypes.pageView, collectPageParameters(router.pathname))
 | 
					      return jitsu.track(telemetryEventTypes.pageView, collectPageParameters(router.pathname));
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
    }, [telemetry])
 | 
					  }, [telemetry]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const toggleProfileDropdown = () => {
 | 
					  const toggleProfileDropdown = () => {
 | 
				
			||||||
        setProfileDropdownExpanded(!profileDropdownExpanded);
 | 
					    setProfileDropdownExpanded(!profileDropdownExpanded);
 | 
				
			||||||
    }
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const toggleMobileMenu = () => {
 | 
					  const toggleMobileMenu = () => {
 | 
				
			||||||
        setMobileMenuExpanded(!mobileMenuExpanded);
 | 
					    setMobileMenuExpanded(!mobileMenuExpanded);
 | 
				
			||||||
    }
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const logoutHandler = () => {
 | 
					  const logoutHandler = () => {
 | 
				
			||||||
        signOut({ redirect: false }).then( () => router.push('/auth/logout') );
 | 
					    signOut({ redirect: false }).then(() => router.push("/auth/logout"));
 | 
				
			||||||
    }
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ( ! loading && ! session ) {
 | 
					  if (!loading && !session) {
 | 
				
			||||||
        router.replace('/auth/login');
 | 
					    router.replace("/auth/login");
 | 
				
			||||||
    }
 | 
					  } else if (loading) {
 | 
				
			||||||
 | 
					    return <p className="text-gray-400">Loading...</p>;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return session && (
 | 
					  return (
 | 
				
			||||||
        <div>
 | 
					    <div>
 | 
				
			||||||
            <div className="bg-gradient-to-b from-blue-600 via-blue-600 to-blue-300 pb-32">
 | 
					      <div className="bg-gradient-to-b from-blue-600 via-blue-600 to-blue-300 pb-32">
 | 
				
			||||||
                <nav className="bg-blue-600">
 | 
					        <nav className="bg-blue-600">
 | 
				
			||||||
                    <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
 | 
					          <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
 | 
				
			||||||
                        <div className="border-b border-blue-500">
 | 
					            <div className="border-b border-blue-500">
 | 
				
			||||||
                            <div className="flex items-center justify-between h-16 px-4 sm:px-0">
 | 
					              <div className="flex items-center justify-between h-16 px-4 sm:px-0">
 | 
				
			||||||
                                <div className="flex items-center">
 | 
					                <div className="flex items-center">
 | 
				
			||||||
                                    <div className="flex-shrink-0">
 | 
					                  <div className="flex-shrink-0">
 | 
				
			||||||
                                        <img className="h-6" src="/calendso-white.svg" alt="Calendso" />
 | 
					                    <img className="h-6" src="/calendso-white.svg" alt="Calendso" />
 | 
				
			||||||
                                    </div>
 | 
					                  </div>
 | 
				
			||||||
                                    <div className="hidden md:block">
 | 
					                  <div className="hidden md:block">
 | 
				
			||||||
                                        <div className="ml-10 flex items-baseline space-x-4">
 | 
					                    <div className="ml-10 flex items-baseline space-x-4">
 | 
				
			||||||
                                            <Link href="/">
 | 
					                      <Link href="/">
 | 
				
			||||||
                                                <a className={router.pathname == "/" ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium" : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Dashboard</a>
 | 
					                        <a
 | 
				
			||||||
                                            </Link>
 | 
					                          className={
 | 
				
			||||||
                                            {/* <Link href="/">
 | 
					                            router.pathname == "/"
 | 
				
			||||||
 | 
					                              ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
 | 
					                              : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
 | 
					                          }>
 | 
				
			||||||
 | 
					                          Dashboard
 | 
				
			||||||
 | 
					                        </a>
 | 
				
			||||||
 | 
					                      </Link>
 | 
				
			||||||
 | 
					                      {/* <Link href="/">
 | 
				
			||||||
                                                <a className={router.pathname.startsWith("/bookings") ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium" : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Bookings</a>
 | 
					                                                <a className={router.pathname.startsWith("/bookings") ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium" : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Bookings</a>
 | 
				
			||||||
                                            </Link> */}
 | 
					                                            </Link> */}
 | 
				
			||||||
                                            <Link href="/availability">
 | 
					                      <Link href="/availability">
 | 
				
			||||||
                                                <a className={router.pathname.startsWith("/availability") ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium" : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Availability</a>
 | 
					                        <a
 | 
				
			||||||
                                            </Link>
 | 
					                          className={
 | 
				
			||||||
                                            <Link href="/integrations">
 | 
					                            router.pathname.startsWith("/availability")
 | 
				
			||||||
                                                <a className={router.pathname.startsWith("/integrations") ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium" : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Integrations</a>
 | 
					                              ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
                                            </Link>
 | 
					                              : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
                                            <Link href="/settings/profile">
 | 
					                          }>
 | 
				
			||||||
                                                <a className={router.pathname.startsWith("/settings") ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium" : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Settings</a>
 | 
					                          Availability
 | 
				
			||||||
                                            </Link>
 | 
					                        </a>
 | 
				
			||||||
                                        </div>
 | 
					                      </Link>
 | 
				
			||||||
                                    </div>
 | 
					                      <Link href="/integrations">
 | 
				
			||||||
                                </div>
 | 
					                        <a
 | 
				
			||||||
                                <div className="hidden md:block">
 | 
					                          className={
 | 
				
			||||||
                                    <div className="ml-4 flex items-center md:ml-6">
 | 
					                            router.pathname.startsWith("/integrations")
 | 
				
			||||||
                                        <div className="ml-3 relative">
 | 
					                              ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
                                            <div>
 | 
					                              : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
                                                <button onClick={toggleProfileDropdown} type="button" className="max-w-xs bg-gray-800 rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white" id="user-menu" aria-expanded="false" aria-haspopup="true">
 | 
					                          }>
 | 
				
			||||||
                                                    <span className="sr-only">Open user menu</span>
 | 
					                          Integrations
 | 
				
			||||||
                                                    <img className="h-8 w-8 rounded-full" src={session.user.image ? session.user.image : "https://eu.ui-avatars.com/api/?background=fff&color=039be5&name=" + encodeURIComponent(session.user.name || "")} alt="" />
 | 
					                        </a>
 | 
				
			||||||
                                                </button>
 | 
					                      </Link>
 | 
				
			||||||
                                            </div>
 | 
					                      <Link href="/settings/profile">
 | 
				
			||||||
                                            {
 | 
					                        <a
 | 
				
			||||||
                                                profileDropdownExpanded && (
 | 
					                          className={
 | 
				
			||||||
                                                    <div className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-50" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
 | 
					                            router.pathname.startsWith("/settings")
 | 
				
			||||||
                                                        <Link href={"/" + session.user.username}><a target="_blank" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Your Public Page</a></Link>
 | 
					                              ? "bg-blue-500 transition-colors duration-300 ease-in-out text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
                                                        <Link href="/settings/profile"><a className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Your Profile</a></Link>
 | 
					                              : "text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out hover:text-white px-3 py-2 rounded-md text-sm font-medium"
 | 
				
			||||||
                                                        <Link href="/settings/password"><a className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Login & Security</a></Link>
 | 
					                          }>
 | 
				
			||||||
                                                        <button onClick={logoutHandler} className="w-full text-left block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">Sign out</button>
 | 
					                          Settings
 | 
				
			||||||
                                                    </div>
 | 
					                        </a>
 | 
				
			||||||
                                                )
 | 
					                      </Link>
 | 
				
			||||||
                                            }
 | 
					 | 
				
			||||||
                                        </div>
 | 
					 | 
				
			||||||
                                    </div>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                                <div className="-mr-2 flex md:hidden">
 | 
					 | 
				
			||||||
                                    <button onClick={toggleMobileMenu} type="button" className=" inline-flex items-center justify-center p-2 rounded-md text-white focus:outline-none" aria-controls="mobile-menu" aria-expanded="false">
 | 
					 | 
				
			||||||
                                        <span className="sr-only">Open main menu</span>
 | 
					 | 
				
			||||||
                                        { !mobileMenuExpanded && <MenuIcon className="block h-6 w-6" /> }
 | 
					 | 
				
			||||||
                                        { mobileMenuExpanded && <XIcon className="block h-6 w-6" /> }
 | 
					 | 
				
			||||||
                                    </button>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
                    { mobileMenuExpanded && <div className="border-b border-blue-500 md:hidden bg-blue-600" id="mobile-menu">
 | 
					 | 
				
			||||||
                        <div className="px-2 py-3 space-y-1 sm:px-3">
 | 
					 | 
				
			||||||
                            <Link href="/">
 | 
					 | 
				
			||||||
                                <a className={router.pathname == "/" ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium" : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"}>Dashboard</a>
 | 
					 | 
				
			||||||
                            </Link>
 | 
					 | 
				
			||||||
                            <Link href="/availability">
 | 
					 | 
				
			||||||
                                <a className={router.pathname.startsWith("/availability") ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium" : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"}>Availability</a>
 | 
					 | 
				
			||||||
                            </Link>
 | 
					 | 
				
			||||||
                            <Link href="/integrations">
 | 
					 | 
				
			||||||
                                <a className={router.pathname.startsWith("/integrations") ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium" : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"}>Integrations</a>
 | 
					 | 
				
			||||||
                            </Link>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                        <div className="pt-4 pb-3 border-t border-blue-500">
 | 
					 | 
				
			||||||
                            <div className="flex items-center px-5">
 | 
					 | 
				
			||||||
                                <div className="flex-shrink-0">
 | 
					 | 
				
			||||||
                                    <img className="h-10 w-10 rounded-full" src={"https://eu.ui-avatars.com/api/?background=039be5&color=fff&name=" + encodeURIComponent(session.user.name || session.user.username)} alt="" />
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                                <div className="ml-3">
 | 
					 | 
				
			||||||
                                    <div className="text-base font-medium leading-none text-white">{session.user.name || session.user.username}</div>
 | 
					 | 
				
			||||||
                                    <div className="text-sm font-medium leading-none text-gray-200">{session.user.email}</div>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                            <div className="mt-3 px-2 space-y-1">
 | 
					 | 
				
			||||||
                                <Link href="/settings/profile">
 | 
					 | 
				
			||||||
                                    <a className="block px-3 py-2 rounded-md text-base font-medium text-gray-100 hover:text-white hover:bg-gray-700">Your Profile</a>
 | 
					 | 
				
			||||||
                                </Link>
 | 
					 | 
				
			||||||
                                <Link href="/settings">
 | 
					 | 
				
			||||||
                                    <a className={router.pathname.startsWith("/settings") ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium" : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"}>Settings</a>
 | 
					 | 
				
			||||||
                                </Link>
 | 
					 | 
				
			||||||
                                <button onClick={logoutHandler} className="block w-full text-left px-3 py-2 rounded-md text-base font-medium text-gray-100 hover:text-white hover:bg-gray-700">Sign out</button>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                </nav>
 | 
					 | 
				
			||||||
                <header className={props.noPaddingBottom ? "pt-10" : "py-10"}>
 | 
					 | 
				
			||||||
                    <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
 | 
					 | 
				
			||||||
                        <h1 className="text-3xl font-bold text-white">
 | 
					 | 
				
			||||||
                            {props.heading}
 | 
					 | 
				
			||||||
                        </h1>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </header>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <main className="-mt-32">
 | 
					 | 
				
			||||||
                <div className="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
 | 
					 | 
				
			||||||
                    {props.children}
 | 
					 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </main>
 | 
					                <div className="hidden md:block">
 | 
				
			||||||
        </div>
 | 
					                  <div className="ml-4 flex items-center md:ml-6">
 | 
				
			||||||
);
 | 
					                    <div className="ml-3 relative">
 | 
				
			||||||
 | 
					                      <div>
 | 
				
			||||||
 | 
					                        <button
 | 
				
			||||||
 | 
					                          onClick={toggleProfileDropdown}
 | 
				
			||||||
 | 
					                          type="button"
 | 
				
			||||||
 | 
					                          className="max-w-xs bg-gray-800 rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
 | 
				
			||||||
 | 
					                          id="user-menu"
 | 
				
			||||||
 | 
					                          aria-expanded="false"
 | 
				
			||||||
 | 
					                          aria-haspopup="true">
 | 
				
			||||||
 | 
					                          <span className="sr-only">Open user menu</span>
 | 
				
			||||||
 | 
					                          <img
 | 
				
			||||||
 | 
					                            className="h-8 w-8 rounded-full"
 | 
				
			||||||
 | 
					                            src={
 | 
				
			||||||
 | 
					                              session.user.image
 | 
				
			||||||
 | 
					                                ? session.user.image
 | 
				
			||||||
 | 
					                                : "https://eu.ui-avatars.com/api/?background=fff&color=039be5&name=" +
 | 
				
			||||||
 | 
					                                  encodeURIComponent(session.user.name || "")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            alt=""
 | 
				
			||||||
 | 
					                          />
 | 
				
			||||||
 | 
					                        </button>
 | 
				
			||||||
 | 
					                      </div>
 | 
				
			||||||
 | 
					                      {profileDropdownExpanded && (
 | 
				
			||||||
 | 
					                        <div
 | 
				
			||||||
 | 
					                          className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-50"
 | 
				
			||||||
 | 
					                          role="menu"
 | 
				
			||||||
 | 
					                          aria-orientation="vertical"
 | 
				
			||||||
 | 
					                          aria-labelledby="user-menu">
 | 
				
			||||||
 | 
					                          <Link href={"/" + session.user.username}>
 | 
				
			||||||
 | 
					                            <a
 | 
				
			||||||
 | 
					                              target="_blank"
 | 
				
			||||||
 | 
					                              className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
 | 
				
			||||||
 | 
					                              role="menuitem">
 | 
				
			||||||
 | 
					                              Your Public Page
 | 
				
			||||||
 | 
					                            </a>
 | 
				
			||||||
 | 
					                          </Link>
 | 
				
			||||||
 | 
					                          <Link href="/settings/profile">
 | 
				
			||||||
 | 
					                            <a
 | 
				
			||||||
 | 
					                              className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
 | 
				
			||||||
 | 
					                              role="menuitem">
 | 
				
			||||||
 | 
					                              Your Profile
 | 
				
			||||||
 | 
					                            </a>
 | 
				
			||||||
 | 
					                          </Link>
 | 
				
			||||||
 | 
					                          <Link href="/settings/password">
 | 
				
			||||||
 | 
					                            <a
 | 
				
			||||||
 | 
					                              className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
 | 
				
			||||||
 | 
					                              role="menuitem">
 | 
				
			||||||
 | 
					                              Login & Security
 | 
				
			||||||
 | 
					                            </a>
 | 
				
			||||||
 | 
					                          </Link>
 | 
				
			||||||
 | 
					                          <button
 | 
				
			||||||
 | 
					                            onClick={logoutHandler}
 | 
				
			||||||
 | 
					                            className="w-full text-left block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
 | 
				
			||||||
 | 
					                            role="menuitem">
 | 
				
			||||||
 | 
					                            Sign out
 | 
				
			||||||
 | 
					                          </button>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                      )}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div className="-mr-2 flex md:hidden">
 | 
				
			||||||
 | 
					                  <button
 | 
				
			||||||
 | 
					                    onClick={toggleMobileMenu}
 | 
				
			||||||
 | 
					                    type="button"
 | 
				
			||||||
 | 
					                    className=" inline-flex items-center justify-center p-2 rounded-md text-white focus:outline-none"
 | 
				
			||||||
 | 
					                    aria-controls="mobile-menu"
 | 
				
			||||||
 | 
					                    aria-expanded="false">
 | 
				
			||||||
 | 
					                    <span className="sr-only">Open main menu</span>
 | 
				
			||||||
 | 
					                    {!mobileMenuExpanded && <MenuIcon className="block h-6 w-6" />}
 | 
				
			||||||
 | 
					                    {mobileMenuExpanded && <XIcon className="block h-6 w-6" />}
 | 
				
			||||||
 | 
					                  </button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          {mobileMenuExpanded && (
 | 
				
			||||||
 | 
					            <div className="border-b border-blue-500 md:hidden bg-blue-600" id="mobile-menu">
 | 
				
			||||||
 | 
					              <div className="px-2 py-3 space-y-1 sm:px-3">
 | 
				
			||||||
 | 
					                <Link href="/">
 | 
				
			||||||
 | 
					                  <a
 | 
				
			||||||
 | 
					                    className={
 | 
				
			||||||
 | 
					                      router.pathname == "/"
 | 
				
			||||||
 | 
					                        ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                        : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                    }>
 | 
				
			||||||
 | 
					                    Dashboard
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                </Link>
 | 
				
			||||||
 | 
					                <Link href="/availability">
 | 
				
			||||||
 | 
					                  <a
 | 
				
			||||||
 | 
					                    className={
 | 
				
			||||||
 | 
					                      router.pathname.startsWith("/availability")
 | 
				
			||||||
 | 
					                        ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                        : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                    }>
 | 
				
			||||||
 | 
					                    Availability
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                </Link>
 | 
				
			||||||
 | 
					                <Link href="/integrations">
 | 
				
			||||||
 | 
					                  <a
 | 
				
			||||||
 | 
					                    className={
 | 
				
			||||||
 | 
					                      router.pathname.startsWith("/integrations")
 | 
				
			||||||
 | 
					                        ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                        : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                    }>
 | 
				
			||||||
 | 
					                    Integrations
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                </Link>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <div className="pt-4 pb-3 border-t border-blue-500">
 | 
				
			||||||
 | 
					                <div className="flex items-center px-5">
 | 
				
			||||||
 | 
					                  <div className="flex-shrink-0">
 | 
				
			||||||
 | 
					                    <img
 | 
				
			||||||
 | 
					                      className="h-10 w-10 rounded-full"
 | 
				
			||||||
 | 
					                      src={
 | 
				
			||||||
 | 
					                        "https://eu.ui-avatars.com/api/?background=039be5&color=fff&name=" +
 | 
				
			||||||
 | 
					                        encodeURIComponent(session.user.name || session.user.username)
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                      alt=""
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="ml-3">
 | 
				
			||||||
 | 
					                    <div className="text-base font-medium leading-none text-white">
 | 
				
			||||||
 | 
					                      {session.user.name || session.user.username}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div className="text-sm font-medium leading-none text-gray-200">{session.user.email}</div>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div className="mt-3 px-2 space-y-1">
 | 
				
			||||||
 | 
					                  <Link href="/settings/profile">
 | 
				
			||||||
 | 
					                    <a className="block px-3 py-2 rounded-md text-base font-medium text-gray-100 hover:text-white hover:bg-gray-700">
 | 
				
			||||||
 | 
					                      Your Profile
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                  </Link>
 | 
				
			||||||
 | 
					                  <Link href="/settings">
 | 
				
			||||||
 | 
					                    <a
 | 
				
			||||||
 | 
					                      className={
 | 
				
			||||||
 | 
					                        router.pathname.startsWith("/settings")
 | 
				
			||||||
 | 
					                          ? "bg-blue-500 text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                          : "text-gray-100 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
 | 
				
			||||||
 | 
					                      }>
 | 
				
			||||||
 | 
					                      Settings
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                  </Link>
 | 
				
			||||||
 | 
					                  <button
 | 
				
			||||||
 | 
					                    onClick={logoutHandler}
 | 
				
			||||||
 | 
					                    className="block w-full text-left px-3 py-2 rounded-md text-base font-medium text-gray-100 hover:text-white hover:bg-gray-700">
 | 
				
			||||||
 | 
					                    Sign out
 | 
				
			||||||
 | 
					                  </button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </nav>
 | 
				
			||||||
 | 
					        <header className={props.noPaddingBottom ? "pt-10" : "py-10"}>
 | 
				
			||||||
 | 
					          <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
 | 
				
			||||||
 | 
					            <h1 className="text-3xl font-bold text-white">{props.heading}</h1>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </header>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <main className="-mt-32">
 | 
				
			||||||
 | 
					        <div className="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">{props.children}</div>
 | 
				
			||||||
 | 
					      </main>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const UsernameInput = React.forwardRef( (props, ref) => (
 | 
					const UsernameInput = React.forwardRef((props, ref) => (
 | 
				
			||||||
  // todo, check if username is already taken here?
 | 
					  // todo, check if username is already taken here?
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
    <label htmlFor="username" className="block text-sm font-medium text-gray-700">
 | 
					    <label htmlFor="username" className="block text-sm font-medium text-gray-700">
 | 
				
			||||||
| 
						 | 
					@ -10,8 +10,20 @@ export const UsernameInput = React.forwardRef( (props, ref) => (
 | 
				
			||||||
      <span className="bg-gray-50 border border-r-0 border-gray-300 rounded-l-md px-3 inline-flex items-center text-gray-500 sm:text-sm">
 | 
					      <span className="bg-gray-50 border border-r-0 border-gray-300 rounded-l-md px-3 inline-flex items-center text-gray-500 sm:text-sm">
 | 
				
			||||||
        {typeof window !== "undefined" && window.location.hostname}/
 | 
					        {typeof window !== "undefined" && window.location.hostname}/
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
      <input ref={ref} type="text" name="username" id="username" autoComplete="username" required {...props}
 | 
					      <input
 | 
				
			||||||
             className="focus:ring-blue-500 focus:border-blue-500 flex-grow block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300"/>
 | 
					        ref={ref}
 | 
				
			||||||
 | 
					        type="text"
 | 
				
			||||||
 | 
					        name="username"
 | 
				
			||||||
 | 
					        id="username"
 | 
				
			||||||
 | 
					        autoComplete="username"
 | 
				
			||||||
 | 
					        required
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					        className="focus:ring-blue-500 focus:border-blue-500 flex-grow block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300 lowercase"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
));
 | 
					));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UsernameInput.displayName = "UsernameInput";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { UsernameInput };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,140 +1,173 @@
 | 
				
			||||||
import Head from 'next/head';
 | 
					import { GetServerSideProps } from "next";
 | 
				
			||||||
import Link from 'next/link';
 | 
					import Head from "next/head";
 | 
				
			||||||
import { useRef, useState } from 'react';
 | 
					import { useRef, useState } from "react";
 | 
				
			||||||
import { useRouter } from 'next/router';
 | 
					import prisma from "../../lib/prisma";
 | 
				
			||||||
import prisma from '../../lib/prisma';
 | 
					import Modal from "../../components/Modal";
 | 
				
			||||||
import Modal from '../../components/Modal';
 | 
					import Shell from "../../components/Shell";
 | 
				
			||||||
import Shell from '../../components/Shell';
 | 
					import SettingsShell from "../../components/Settings";
 | 
				
			||||||
import SettingsShell from '../../components/Settings';
 | 
					import Avatar from "../../components/Avatar";
 | 
				
			||||||
import Avatar from '../../components/Avatar';
 | 
					import { getSession } from "next-auth/client";
 | 
				
			||||||
import { signIn, useSession, getSession } from 'next-auth/client';
 | 
					import TimezoneSelect from "react-timezone-select";
 | 
				
			||||||
import TimezoneSelect from 'react-timezone-select';
 | 
					import { UsernameInput } from "../../components/ui/UsernameInput";
 | 
				
			||||||
import {UsernameInput} from "../../components/ui/UsernameInput";
 | 
					 | 
				
			||||||
import ErrorAlert from "../../components/ui/alerts/Error";
 | 
					import ErrorAlert from "../../components/ui/alerts/Error";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Settings(props) {
 | 
					export default function Settings(props) {
 | 
				
			||||||
    const [ session, loading ] = useSession();
 | 
					  const [successModalOpen, setSuccessModalOpen] = useState(false);
 | 
				
			||||||
    const router = useRouter();
 | 
					  const usernameRef = useRef<HTMLInputElement>();
 | 
				
			||||||
    const [successModalOpen, setSuccessModalOpen] = useState(false);
 | 
					  const nameRef = useRef<HTMLInputElement>();
 | 
				
			||||||
    const usernameRef = useRef<HTMLInputElement>();
 | 
					  const descriptionRef = useRef<HTMLTextAreaElement>();
 | 
				
			||||||
    const nameRef = useRef<HTMLInputElement>();
 | 
					  const avatarRef = useRef<HTMLInputElement>();
 | 
				
			||||||
    const descriptionRef = useRef<HTMLTextAreaElement>();
 | 
					  const [selectedTimeZone, setSelectedTimeZone] = useState({ value: props.user.timeZone });
 | 
				
			||||||
    const avatarRef = useRef<HTMLInputElement>();
 | 
					  const [selectedWeekStartDay, setSelectedWeekStartDay] = useState(props.user.weekStart || "Sunday");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [ selectedTimeZone, setSelectedTimeZone ] = useState({ value: props.user.timeZone });
 | 
					  const [hasErrors, setHasErrors] = useState(false);
 | 
				
			||||||
    const [ selectedWeekStartDay, setSelectedWeekStartDay ] = useState(props.user.weekStart || 'Sunday');
 | 
					  const [errorMessage, setErrorMessage] = useState("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [ hasErrors, setHasErrors ] = useState(false);
 | 
					  const closeSuccessModal = () => {
 | 
				
			||||||
    const [ errorMessage, setErrorMessage ] = useState('');
 | 
					    setSuccessModalOpen(false);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (loading) {
 | 
					  const handleError = async (resp) => {
 | 
				
			||||||
        return <p className="text-gray-400">Loading...</p>;
 | 
					    if (!resp.ok) {
 | 
				
			||||||
 | 
					      const error = await resp.json();
 | 
				
			||||||
 | 
					      throw new Error(error.message);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const closeSuccessModal = () => { setSuccessModalOpen(false); }
 | 
					  async function updateProfileHandler(event) {
 | 
				
			||||||
 | 
					    event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleError = async (resp) => {
 | 
					    const enteredUsername = usernameRef.current.value.toLowerCase();
 | 
				
			||||||
      if (!resp.ok) {
 | 
					    const enteredName = nameRef.current.value;
 | 
				
			||||||
        const error = await resp.json();
 | 
					    const enteredDescription = descriptionRef.current.value;
 | 
				
			||||||
        throw new Error(error.message);
 | 
					    const enteredAvatar = avatarRef.current.value;
 | 
				
			||||||
      }
 | 
					    const enteredTimeZone = selectedTimeZone.value;
 | 
				
			||||||
    }
 | 
					    const enteredWeekStartDay = selectedWeekStartDay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function updateProfileHandler(event) {
 | 
					    // TODO: Add validation
 | 
				
			||||||
        event.preventDefault();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const enteredUsername = usernameRef.current.value;
 | 
					    await fetch("/api/user/profile", {
 | 
				
			||||||
        const enteredName = nameRef.current.value;
 | 
					      method: "PATCH",
 | 
				
			||||||
        const enteredDescription = descriptionRef.current.value;
 | 
					      body: JSON.stringify({
 | 
				
			||||||
        const enteredAvatar = avatarRef.current.value;
 | 
					        username: enteredUsername,
 | 
				
			||||||
        const enteredTimeZone = selectedTimeZone.value;
 | 
					        name: enteredName,
 | 
				
			||||||
        const enteredWeekStartDay = selectedWeekStartDay;
 | 
					        description: enteredDescription,
 | 
				
			||||||
 | 
					        avatar: enteredAvatar,
 | 
				
			||||||
 | 
					        timeZone: enteredTimeZone,
 | 
				
			||||||
 | 
					        weekStart: enteredWeekStartDay,
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
 | 
					      headers: {
 | 
				
			||||||
 | 
					        "Content-Type": "application/json",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					      .then(handleError)
 | 
				
			||||||
 | 
					      .then(() => {
 | 
				
			||||||
 | 
					        setSuccessModalOpen(true);
 | 
				
			||||||
 | 
					        setHasErrors(false); // dismiss any open errors
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch((err) => {
 | 
				
			||||||
 | 
					        setHasErrors(true);
 | 
				
			||||||
 | 
					        setErrorMessage(err.message);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TODO: Add validation
 | 
					  return (
 | 
				
			||||||
 | 
					    <Shell heading="Profile">
 | 
				
			||||||
 | 
					      <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}>
 | 
				
			||||||
 | 
					          {hasErrors && <ErrorAlert message={errorMessage} />}
 | 
				
			||||||
 | 
					          <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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const response = await fetch('/api/user/profile', {
 | 
					            <div className="mt-6 flex flex-col lg:flex-row">
 | 
				
			||||||
            method: 'PATCH',
 | 
					              <div className="flex-grow space-y-6">
 | 
				
			||||||
            body: JSON.stringify({username: enteredUsername, name: enteredName, description: enteredDescription, avatar: enteredAvatar, timeZone: enteredTimeZone, weekStart: enteredWeekStartDay}),
 | 
					                <div className="flex">
 | 
				
			||||||
            headers: {
 | 
					                  <div className="w-1/2 mr-2">
 | 
				
			||||||
                'Content-Type': 'application/json'
 | 
					                    <UsernameInput ref={usernameRef} defaultValue={props.user.username} />
 | 
				
			||||||
            }
 | 
					                  </div>
 | 
				
			||||||
        }).then(handleError).then( () => {
 | 
					                  <div className="w-1/2 ml-2">
 | 
				
			||||||
          setSuccessModalOpen(true);
 | 
					                    <label htmlFor="name" className="block text-sm font-medium text-gray-700">
 | 
				
			||||||
          setHasErrors(false); // dismiss any open errors
 | 
					                      Full name
 | 
				
			||||||
        }).catch( (err) => {
 | 
					                    </label>
 | 
				
			||||||
          setHasErrors(true);
 | 
					                    <input
 | 
				
			||||||
          setErrorMessage(err.message);
 | 
					                      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}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return(
 | 
					                <div>
 | 
				
			||||||
        <Shell heading="Profile">
 | 
					                  <label htmlFor="about" className="block text-sm font-medium text-gray-700">
 | 
				
			||||||
            <Head>
 | 
					                    About
 | 
				
			||||||
                <title>Profile | Calendso</title>
 | 
					                  </label>
 | 
				
			||||||
                <link rel="icon" href="/favicon.ico" />
 | 
					                  <div className="mt-1">
 | 
				
			||||||
            </Head>
 | 
					                    <textarea
 | 
				
			||||||
            <SettingsShell>
 | 
					                      ref={descriptionRef}
 | 
				
			||||||
                <form className="divide-y divide-gray-200 lg:col-span-9" onSubmit={updateProfileHandler}>
 | 
					                      id="about"
 | 
				
			||||||
                    {hasErrors && <ErrorAlert message={errorMessage} />}
 | 
					                      name="about"
 | 
				
			||||||
                    <div className="py-6 px-4 sm:p-6 lg:pb-8">
 | 
					                      placeholder="A little something about yourself."
 | 
				
			||||||
                        <div>
 | 
					                      rows={3}
 | 
				
			||||||
                            <h2 className="text-lg leading-6 font-medium text-gray-900">Profile</h2>
 | 
					                      className="shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md">
 | 
				
			||||||
                            <p className="mt-1 text-sm text-gray-500">
 | 
					                      {props.user.bio}
 | 
				
			||||||
                                Review and change your public page details.
 | 
					                    </textarea>
 | 
				
			||||||
                            </p>
 | 
					                  </div>
 | 
				
			||||||
                        </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div>
 | 
				
			||||||
 | 
					                  <label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
 | 
				
			||||||
 | 
					                    Timezone
 | 
				
			||||||
 | 
					                  </label>
 | 
				
			||||||
 | 
					                  <div className="mt-1">
 | 
				
			||||||
 | 
					                    <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"
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <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>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <div className="mt-6 flex flex-col lg:flex-row">
 | 
					              <div className="mt-6 flex-grow lg:mt-0 lg:ml-6 lg:flex-grow-0 lg:flex-shrink-0">
 | 
				
			||||||
                            <div className="flex-grow space-y-6">
 | 
					                <p className="mb-2 text-sm font-medium text-gray-700" aria-hidden="true">
 | 
				
			||||||
                                <div className="flex">
 | 
					                  Photo
 | 
				
			||||||
                                    <div className="w-1/2 mr-2">
 | 
					                </p>
 | 
				
			||||||
                                        <UsernameInput ref={usernameRef} defaultValue={props.user.username} />
 | 
					                <div className="mt-1 lg:hidden">
 | 
				
			||||||
                                    </div>
 | 
					                  <div className="flex items-center">
 | 
				
			||||||
                                    <div className="w-1/2 ml-2">
 | 
					                    <div
 | 
				
			||||||
                                        <label htmlFor="name" className="block text-sm font-medium text-gray-700">Full name</label>
 | 
					                      className="flex-shrink-0 inline-block rounded-full overflow-hidden h-12 w-12"
 | 
				
			||||||
                                        <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} />
 | 
					                      aria-hidden="true">
 | 
				
			||||||
                                    </div>
 | 
					                      <Avatar user={props.user} className="rounded-full h-full w-full" />
 | 
				
			||||||
                                </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    {/* <div className="ml-5 rounded-md shadow-sm">
 | 
				
			||||||
                                <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>
 | 
					 | 
				
			||||||
                                <div>
 | 
					 | 
				
			||||||
                                    <label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
 | 
					 | 
				
			||||||
                                        Timezone
 | 
					 | 
				
			||||||
                                    </label>
 | 
					 | 
				
			||||||
                                    <div className="mt-1">
 | 
					 | 
				
			||||||
                                        <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" />
 | 
					 | 
				
			||||||
                                    </div>
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                                <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>
 | 
					 | 
				
			||||||
                                </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">
 | 
					 | 
				
			||||||
                                            <Avatar user={props.user} className="rounded-full h-full w-full" />
 | 
					 | 
				
			||||||
                                        </div>
 | 
					 | 
				
			||||||
                                        {/* <div className="ml-5 rounded-md shadow-sm">
 | 
					 | 
				
			||||||
                                            <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">
 | 
					                                            <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">
 | 
					                                                <label htmlFor="user_photo" className="relative text-sm leading-4 font-medium text-gray-700 pointer-events-none">
 | 
				
			||||||
                                                    <span>Change</span>
 | 
					                                                    <span>Change</span>
 | 
				
			||||||
| 
						 | 
					@ -143,64 +176,81 @@ export default function Settings(props) {
 | 
				
			||||||
                                                <input id="user_photo" name="user_photo" type="file" className="absolute w-full h-full opacity-0 cursor-pointer border-gray-300 rounded-md" />
 | 
					                                                <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>
 | 
					                                            </div>
 | 
				
			||||||
                                        </div> */}
 | 
					                                        </div> */}
 | 
				
			||||||
                                    </div>
 | 
					                  </div>
 | 
				
			||||||
                                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <div className="hidden relative rounded-full overflow-hidden lg:block">
 | 
					                <div className="hidden relative rounded-full overflow-hidden lg:block">
 | 
				
			||||||
                                    <Avatar
 | 
					                  <Avatar
 | 
				
			||||||
                                        user={props.user}
 | 
					                    user={props.user}
 | 
				
			||||||
                                        className="relative rounded-full w-40 h-40"
 | 
					                    className="relative rounded-full w-40 h-40"
 | 
				
			||||||
                                        fallback={<div className="relative bg-blue-600 rounded-full w-40 h-40"></div>}
 | 
					                    fallback={<div className="relative bg-blue-600 rounded-full w-40 h-40"></div>}
 | 
				
			||||||
                                    />
 | 
					                  />
 | 
				
			||||||
                                    {/* <label htmlFor="user-photo" className="absolute inset-0 w-full h-full bg-black bg-opacity-75 flex items-center justify-center text-sm font-medium text-white opacity-0 hover:opacity-100 focus-within:opacity-100">
 | 
					                  {/* <label htmlFor="user-photo" className="absolute inset-0 w-full h-full bg-black bg-opacity-75 flex items-center justify-center text-sm font-medium text-white opacity-0 hover:opacity-100 focus-within:opacity-100">
 | 
				
			||||||
                                        <span>Change</span>
 | 
					                                        <span>Change</span>
 | 
				
			||||||
                                        <span className="sr-only"> user photo</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" />
 | 
					                                        <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" />
 | 
				
			||||||
                                    </label> */}
 | 
					                                    </label> */}
 | 
				
			||||||
                                </div>
 | 
					                </div>
 | 
				
			||||||
                                <div className="mt-4">
 | 
					                <div className="mt-4">
 | 
				
			||||||
                                    <label htmlFor="avatar" className="block text-sm font-medium text-gray-700">Avatar URL</label>
 | 
					                  <label htmlFor="avatar" className="block text-sm font-medium text-gray-700">
 | 
				
			||||||
                                    <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} />
 | 
					                    Avatar URL
 | 
				
			||||||
                                </div>
 | 
					                  </label>
 | 
				
			||||||
                            </div>
 | 
					                  <input
 | 
				
			||||||
                        </div>
 | 
					                    ref={avatarRef}
 | 
				
			||||||
                        <hr className="mt-8" />
 | 
					                    type="text"
 | 
				
			||||||
                        <div className="py-4 flex justify-end">
 | 
					                    name="avatar"
 | 
				
			||||||
                            <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">
 | 
					                    id="avatar"
 | 
				
			||||||
                                Save
 | 
					                    placeholder="URL"
 | 
				
			||||||
                            </button>
 | 
					                    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"
 | 
				
			||||||
                        </div>
 | 
					                    defaultValue={props.user.avatar}
 | 
				
			||||||
                    </div>
 | 
					                  />
 | 
				
			||||||
                </form>
 | 
					                </div>
 | 
				
			||||||
                <Modal heading="Profile updated successfully" description="Your user profile has been updated successfully." open={successModalOpen} handleClose={closeSuccessModal} />
 | 
					              </div>
 | 
				
			||||||
            </SettingsShell>
 | 
					            </div>
 | 
				
			||||||
        </Shell>
 | 
					            <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>
 | 
				
			||||||
 | 
					        <Modal
 | 
				
			||||||
 | 
					          heading="Profile updated successfully"
 | 
				
			||||||
 | 
					          description="Your user profile has been updated successfully."
 | 
				
			||||||
 | 
					          open={successModalOpen}
 | 
				
			||||||
 | 
					          handleClose={closeSuccessModal}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </SettingsShell>
 | 
				
			||||||
 | 
					    </Shell>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getServerSideProps(context) {
 | 
					export const getServerSideProps: GetServerSideProps = async (context) => {
 | 
				
			||||||
    const session = await getSession(context);
 | 
					  const session = await getSession(context);
 | 
				
			||||||
    if (!session) {
 | 
					  if (!session) {
 | 
				
			||||||
        return { redirect: { permanent: false, destination: '/auth/login' } };
 | 
					    return { redirect: { permanent: false, destination: "/auth/login" } };
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const user = await prisma.user.findFirst({
 | 
					  const user = await prisma.user.findFirst({
 | 
				
			||||||
        where: {
 | 
					    where: {
 | 
				
			||||||
            email: session.user.email,
 | 
					      email: session.user.email,
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        select: {
 | 
					    select: {
 | 
				
			||||||
            id: true,
 | 
					      id: true,
 | 
				
			||||||
            username: true,
 | 
					      username: true,
 | 
				
			||||||
            name: true,
 | 
					      name: true,
 | 
				
			||||||
            email: true,
 | 
					      email: true,
 | 
				
			||||||
            bio: true,
 | 
					      bio: true,
 | 
				
			||||||
            avatar: true,
 | 
					      avatar: true,
 | 
				
			||||||
            timeZone: true,
 | 
					      timeZone: true,
 | 
				
			||||||
            weekStart: true,
 | 
					      weekStart: true,
 | 
				
			||||||
        }
 | 
					    },
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					  return {
 | 
				
			||||||
      props: {user}, // will be passed to the page component as props
 | 
					    props: { user }, // will be passed to the page component as props
 | 
				
			||||||
    }
 | 
					  };
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue