Update dashboard
This commit is contained in:
		
							parent
							
								
									209791d86d
								
							
						
					
					
						commit
						3a6eae8b8f
					
				
					 4 changed files with 235 additions and 27 deletions
				
			
		|  | @ -36,10 +36,10 @@ export default function Shell(props) { | |||
| 
 | ||||
|     return session && ( | ||||
|         <div> | ||||
|             <div className="bg-gray-800 pb-32"> | ||||
|                 <nav className="bg-gray-800"> | ||||
|             <div className="bg-gradient-to-b from-blue-600 via-blue-600 to-blue-300 pb-32"> | ||||
|                 <nav className="bg-blue-600"> | ||||
|                     <div className="max-w-7xl mx-auto sm:px-6 lg:px-8"> | ||||
|                         <div className="border-b border-gray-700"> | ||||
|                         <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"> | ||||
|                                     <div className="flex-shrink-0"> | ||||
|  | @ -48,19 +48,19 @@ export default function Shell(props) { | |||
|                                     <div className="hidden md:block"> | ||||
|                                         <div className="ml-10 flex items-baseline space-x-4"> | ||||
|                                             <Link href="/"> | ||||
|                                                 <a className={router.pathname == "/" ? "bg-gray-700 text-white px-3 py-2 rounded-md text-sm font-medium" : "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Dashboard</a> | ||||
|                                                 <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> | ||||
|                                             </Link> | ||||
|                                             {/* <Link href="/"> | ||||
|                                                 <a className={router.pathname.startsWith("/bookings") ? "bg-gray-700 text-white px-3 py-2 rounded-md text-sm font-medium" : "text-gray-300 hover:bg-gray-700 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 href="/availability"> | ||||
|                                                 <a className={router.pathname.startsWith("/availability") ? "bg-gray-700 text-white px-3 py-2 rounded-md text-sm font-medium" : "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Availability</a> | ||||
|                                                 <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> | ||||
|                                             </Link> | ||||
|                                             <Link href="/integrations"> | ||||
|                                                 <a className={router.pathname.startsWith("/integrations") ? "bg-gray-700 text-white px-3 py-2 rounded-md text-sm font-medium" : "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Integrations</a> | ||||
|                                                 <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> | ||||
|                                             </Link> | ||||
|                                             <Link href="/settings/profile"> | ||||
|                                                 <a className={router.pathname.startsWith("/settings") ? "bg-gray-700 text-white px-3 py-2 rounded-md text-sm font-medium" : "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"}>Settings</a> | ||||
|                                                 <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> | ||||
|                                             </Link> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|  | @ -71,7 +71,7 @@ export default function Shell(props) { | |||
|                                             <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={"https://eu.ui-avatars.com/api/?background=039be5&color=fff&name=" + encodeURIComponent(session.user.name || "")} alt="" /> | ||||
|                                                     <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> | ||||
|                                             { | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ export default NextAuth({ | |||
|                     throw new Error('Incorrect password'); | ||||
|                 } | ||||
| 
 | ||||
|                 return {id: user.id, username: user.username, email: user.email, name: user.name}; | ||||
|                 return {id: user.id, username: user.username, email: user.email, name: user.name, image: user.avatar}; | ||||
|             } | ||||
|         }) | ||||
|     ], | ||||
|  |  | |||
|  | @ -110,7 +110,7 @@ export default function Availability(props) { | |||
|                         Event Types | ||||
|                     </h3> | ||||
|                     <div className="mt-3 sm:mt-0 sm:ml-4"> | ||||
|                         <button onClick={toggleAddModal} type="button" className="btn-sm btn-primary"> | ||||
|                         <button onClick={toggleAddModal} type="button" className="btn-sm btn-white"> | ||||
|                             New event type | ||||
|                         </button> | ||||
|                     </div> | ||||
|  |  | |||
							
								
								
									
										240
									
								
								pages/index.tsx
									
									
									
									
									
								
							
							
						
						
									
										240
									
								
								pages/index.tsx
									
									
									
									
									
								
							|  | @ -3,14 +3,66 @@ import Link from 'next/link'; | |||
| import prisma from '../lib/prisma'; | ||||
| import Shell from '../components/Shell'; | ||||
| import { signIn, useSession, getSession } from 'next-auth/client'; | ||||
| import { ClockIcon, CheckIcon, InformationCircleIcon } from '@heroicons/react/outline'; | ||||
| 
 | ||||
| function classNames(...classes) { | ||||
|     return classes.filter(Boolean).join(' ') | ||||
| } | ||||
| 
 | ||||
| export default function Home(props) { | ||||
|     const [ session, loading ] = useSession(); | ||||
|     const [session, loading] = useSession(); | ||||
|     if (loading) { | ||||
|         return <p className="text-gray-400">Loading...</p>; | ||||
|     } | ||||
| 
 | ||||
|     return( | ||||
|     function convertMinsToHrsMins(mins) { | ||||
|         let h = Math.floor(mins / 60); | ||||
|         let m = mins % 60; | ||||
|         h = h < 10 ? '0' + h : h; | ||||
|         m = m < 10 ? '0' + m : m; | ||||
|         return `${h}:${m}`; | ||||
|     } | ||||
| 
 | ||||
|     const stats = [ | ||||
|         { name: 'Event Types', stat: props.eventTypeCount }, | ||||
|         { name: 'Integrations', stat: props.integrationCount }, | ||||
|         { name: 'Available Hours', stat: (props.user.endTime - props.user.startTime) / 60 + ' hours' }, | ||||
|     ]; | ||||
| 
 | ||||
|     let timeline = []; | ||||
| 
 | ||||
|     if (session) { | ||||
|         timeline = [ | ||||
|             { | ||||
|               id: 1, | ||||
|               content: 'Add your first', | ||||
|               target: 'integration', | ||||
|               href: '/integrations', | ||||
|               icon: props.integrationCount != 0 ? CheckIcon : InformationCircleIcon, | ||||
|               iconBackground: props.integrationCount != 0 ? 'bg-green-400' : 'bg-gray-400', | ||||
|             }, | ||||
|             { | ||||
|               id: 2, | ||||
|               content: 'Add one or more', | ||||
|               target: 'event types', | ||||
|               href: '/availability', | ||||
|               icon: props.eventTypeCount != 0 ? CheckIcon : InformationCircleIcon, | ||||
|               iconBackground: props.eventTypeCount != 0 ? 'bg-green-400' : 'bg-gray-400', | ||||
|             }, | ||||
|             { | ||||
|               id: 3, | ||||
|               content: 'Complete your', | ||||
|               target: 'profile', | ||||
|               href: '/settings/profile', | ||||
|               icon: session.user.image ? CheckIcon : InformationCircleIcon, | ||||
|               iconBackground: session.user.image ? 'bg-green-400' : 'bg-gray-400', | ||||
|             }, | ||||
|         ]; | ||||
|     } else { | ||||
|         timeline = []; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|         <div> | ||||
|             <Head> | ||||
|                 <title>Calendso</title> | ||||
|  | @ -20,26 +72,124 @@ export default function Home(props) { | |||
|             <Shell heading="Dashboard"> | ||||
|                 <div className="grid grid-cols-3 gap-4"> | ||||
|                     <div className="col-span-2"> | ||||
|                         <div className="bg-white shadow rounded-lg"> | ||||
|                             <div className="px-4 py-5 sm:p-6"> | ||||
|                         <div className="rounded-lg bg-white shadow"> | ||||
|                             <div className="pt-5 pb-2 px-6 sm:flex sm:items-center sm:justify-between"> | ||||
|                                 <h3 className="text-lg leading-6 font-medium text-gray-900"> | ||||
|                                     Welcome to Calendso! | ||||
|                                     Your stats | ||||
|                                 </h3> | ||||
|                                 <div className="mt-2 max-w-xl text-sm text-gray-500"> | ||||
|                                     <p> | ||||
|                                         Get started by connecting your first calendar integration, enabling us to fetch your availability. Head over to the integrations page, and click the add link. | ||||
|                                     </p> | ||||
|                             </div> | ||||
|                             <dl className="grid grid-cols-1 overflow-hidden divide-y divide-gray-200 md:grid-cols-3 md:divide-y-0 md:divide-x"> | ||||
|                                 {stats.map((item) => ( | ||||
|                                     <div key={item.name} className="px-4 py-5 sm:p-6"> | ||||
|                                         <dt className="text-base font-normal text-gray-900">{item.name}</dt> | ||||
|                                         <dd className="mt-1 flex justify-between items-baseline md:block lg:flex"> | ||||
|                                             <div className="flex items-baseline text-2xl font-semibold text-blue-600"> | ||||
|                                                 {item.stat} | ||||
|                                             </div> | ||||
|                                         </dd> | ||||
|                                     </div> | ||||
|                                 ))} | ||||
|                             </dl> | ||||
|                         </div> | ||||
|                         <div className="mt-8 bg-white shadow overflow-hidden sm:rounded-md"> | ||||
|                             <div className="pt-5 pb-2 px-6 sm:flex sm:items-center sm:justify-between"> | ||||
|                                 <h3 className="text-lg leading-6 font-medium text-gray-900"> | ||||
|                                     Your event types | ||||
|                                 </h3> | ||||
|                             </div> | ||||
|                             <ul className="divide-y divide-gray-200"> | ||||
|                                 {props.eventTypes.map((type) => ( | ||||
|                                     <li key={type.id}> | ||||
|                                         <div className="px-4 py-4 flex items-center sm:px-6"> | ||||
|                                             <div className="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between"> | ||||
|                                                 <div className="truncate"> | ||||
|                                                     <div className="flex text-sm"> | ||||
|                                                         <p className="font-medium text-blue-600 truncate">{type.title}</p> | ||||
|                                                         <p className="ml-1 flex-shrink-0 font-normal text-gray-500">in {type.description}</p> | ||||
|                                                     </div> | ||||
|                                                     <div className="mt-2 flex"> | ||||
|                                                         <div className="flex items-center text-sm text-gray-500"> | ||||
|                                                             <ClockIcon className="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400" aria-hidden="true" /> | ||||
|                                                             <p> | ||||
|                                                                 {type.length} minutes | ||||
|                                                             </p> | ||||
|                                                         </div> | ||||
|                                                     </div> | ||||
|                                                 </div> | ||||
|                                             </div> | ||||
|                                             <div className="ml-5 flex-shrink-0"> | ||||
|                                                 <Link href={"/" + session.user.username + "/" + type.slug}><a target="_blank" className="text-blue-600 hover:text-blue-900 mr-2 font-medium">View</a></Link> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </li> | ||||
|                                 ))} | ||||
|                             </ul> | ||||
|                         </div> | ||||
|                         <div className="mt-8 bg-white shadow overflow-hidden sm:rounded-md p-6"> | ||||
|                             <div className="flex"> | ||||
|                                 <div className="w-1/2 self-center"> | ||||
|                                     <h2 className="text-2xl font-semibold">Getting started</h2> | ||||
|                                     <p className="text-gray-600 text-sm">Steps you should take to get started with Calendso.</p> | ||||
|                                 </div> | ||||
|                                 <div className="mt-3 text-sm"> | ||||
|                                     <Link href="/integrations"> | ||||
|                                         <a className="font-medium text-blue-600 hover:text-blue-500"> Set up your first integration <span aria-hidden="true">→</span></a> | ||||
|                                     </Link> | ||||
|                                 <div className="w-1/2"> | ||||
|                                     <div className="flow-root"> | ||||
|                                         <ul className="-mb-8"> | ||||
|                                             {timeline.map((event, eventIdx) => ( | ||||
|                                                 <li key={event.id}> | ||||
|                                                     <div className="relative pb-8"> | ||||
|                                                         {eventIdx !== timeline.length - 1 ? ( | ||||
|                                                             <span className="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true" /> | ||||
|                                                         ) : null} | ||||
|                                                         <div className="relative flex space-x-3"> | ||||
|                                                             <div> | ||||
|                                                                 <span | ||||
|                                                                     className={classNames( | ||||
|                                                                         event.iconBackground, | ||||
|                                                                         'h-8 w-8 rounded-full flex items-center justify-center ring-8 ring-white' | ||||
|                                                                     )} | ||||
|                                                                 > | ||||
|                                                                     <event.icon className="h-5 w-5 text-white" aria-hidden="true" /> | ||||
|                                                                 </span> | ||||
|                                                             </div> | ||||
|                                                             <div className="min-w-0 flex-1 pt-1.5 flex justify-between space-x-4"> | ||||
|                                                                 <div> | ||||
|                                                                     <p className="text-sm text-gray-500"> | ||||
|                                                                         {event.content}{' '} | ||||
|                                                                         <Link href={event.href}> | ||||
|                                                                             <a className="font-medium text-gray-900"> | ||||
|                                                                                 {event.target} | ||||
|                                                                             </a> | ||||
|                                                                         </Link> | ||||
|                                                                     </p> | ||||
|                                                                 </div> | ||||
|                                                             </div> | ||||
|                                                         </div> | ||||
|                                                     </div> | ||||
|                                                 </li> | ||||
|                                             ))} | ||||
|                                         </ul> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                         <div className="bg-white rounded-lg shadow px-5 py-6 md:py-7 sm:px-6"> | ||||
|                             <div className="mb-4 sm:flex sm:items-center sm:justify-between"> | ||||
|                                 <h3 className="text-lg leading-6 font-medium text-gray-900"> | ||||
|                                     Your day | ||||
|                                 </h3> | ||||
|                                 <div className="mt-3 sm:mt-0 sm:ml-4"> | ||||
|                                     <Link href="/availability"> | ||||
|                                         <a className="text-sm text-gray-400">Configure</a> | ||||
|                                     </Link> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                             <div> | ||||
|                                 <p className="text-2xl font-semibold text-gray-600">Offering time slots between <span className="text-blue-600">{convertMinsToHrsMins(props.user.startTime)}</span> and <span className="text-blue-600">{convertMinsToHrsMins(props.user.endTime)}</span></p> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div className="mt-8 bg-white rounded-lg shadow px-5 py-6 md:py-7 sm:px-6"> | ||||
|                             <div className="mb-8 sm:flex sm:items-center sm:justify-between"> | ||||
|                                 <h3 className="text-lg leading-6 font-medium text-gray-900"> | ||||
|                                     Your integrations | ||||
|  | @ -69,6 +219,39 @@ export default function Home(props) { | |||
|                                 } | ||||
|                             </ul> | ||||
|                         </div> | ||||
|                         <div className="mt-8 bg-white rounded-lg shadow px-5 py-6 md:py-7 sm:px-6"> | ||||
|                             <div className="mb-4 sm:flex sm:items-center sm:justify-between"> | ||||
|                                 <h3 className="text-lg leading-6 font-medium text-gray-900"> | ||||
|                                     Your event types | ||||
|                                 </h3> | ||||
|                                 <div className="mt-3 sm:mt-0 sm:ml-4"> | ||||
|                                     <Link href="/availability"> | ||||
|                                         <a className="text-sm text-gray-400">View more</a> | ||||
|                                     </Link> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                             <ul className="divide-y divide-gray-200"> | ||||
|                                 {props.eventTypes.map((type) => ( | ||||
|                                     <li | ||||
|                                         key={type.id} | ||||
|                                         className="relative bg-white py-5 hover:bg-gray-50 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600" | ||||
|                                     > | ||||
|                                         <div className="flex justify-between space-x-3"> | ||||
|                                             <div className="min-w-0 flex-1"> | ||||
|                                                 <a href="#" className="block focus:outline-none"> | ||||
|                                                     <span className="absolute inset-0" aria-hidden="true" /> | ||||
|                                                     <p className="text-sm font-medium text-gray-900 truncate">{type.title}</p> | ||||
|                                                     <p className="text-sm text-gray-500 truncate">{type.description}</p> | ||||
|                                                 </a> | ||||
|                                             </div> | ||||
|                                             <span className="flex-shrink-0 whitespace-nowrap text-sm text-gray-500"> | ||||
|                                                 {type.length} minutes | ||||
|                                             </span> | ||||
|                                         </div> | ||||
|                                     </li> | ||||
|                                 ))} | ||||
|                             </ul> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </Shell> | ||||
|  | @ -79,15 +262,22 @@ export default function Home(props) { | |||
| export async function getServerSideProps(context) { | ||||
|     const session = await getSession(context); | ||||
| 
 | ||||
|     let user = []; | ||||
|     let credentials = []; | ||||
|     let eventTypes = []; | ||||
| 
 | ||||
|     let eventTypeCount = 0; | ||||
|     let integrationCount = 0; | ||||
| 
 | ||||
|     if (session) { | ||||
|         const user = await prisma.user.findFirst({ | ||||
|         user = await prisma.user.findFirst({ | ||||
|             where: { | ||||
|                 email: session.user.email, | ||||
|             }, | ||||
|             select: { | ||||
|                 id: true | ||||
|                 id: true, | ||||
|                 startTime: true, | ||||
|                 endTime: true | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|  | @ -99,8 +289,26 @@ export async function getServerSideProps(context) { | |||
|                 type: true | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         eventTypes = await prisma.eventType.findMany({ | ||||
|             where: { | ||||
|                 userId: user.id, | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         eventTypeCount = await prisma.eventType.count({ | ||||
|             where: { | ||||
|                 userId: session.user.id | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         integrationCount = await prisma.credential.count({ | ||||
|             where: { | ||||
|                 userId: session.user.id | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     return { | ||||
|       props: {credentials}, // will be passed to the page component as props
 | ||||
|         props: { user, credentials, eventTypes, eventTypeCount, integrationCount }, // will be passed to the page component as props
 | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in a new issue
	
	 Bailey Pumfleet
						Bailey Pumfleet