-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Improve Nav Bar & use Wasp Link components #311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
6045c61
4def591
93fe59e
cca7dd3
f66a499
970553c
e47b358
0ce8901
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- template/app/migrations/20241030143842_checkout_session_id/migration.sql | ||
+++ opensaas-sh/app/migrations/20241030143842_checkout_session_id/migration.sql | ||
@@ -0,0 +1,8 @@ | ||
+/* | ||
+ Warnings: | ||
+ | ||
+ - You are about to drop the column `checkoutSessionId` on the `User` table. All the data in the column will be lost. | ||
+ | ||
+*/ | ||
+-- AlterTable | ||
+ALTER TABLE "User" DROP COLUMN "checkoutSessionId"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- template/app/src/client/components/NavBar/NavBar.tsx | ||
+++ opensaas-sh/app/src/client/components/NavBar/NavBar.tsx | ||
@@ -31,6 +31,7 @@ | ||
!isLandingPage, | ||
})} | ||
> | ||
+ {isLandingPage && <Announcement />} | ||
<nav className='flex items-center justify-between p-6 lg:px-8' aria-label='Global'> | ||
<div className='flex items-center lg:flex-1'> | ||
<Link | ||
@@ -38,9 +39,7 @@ | ||
className='flex items-center -m-1.5 p-1.5 text-gray-900 duration-300 ease-in-out hover:text-yellow-500' | ||
> | ||
<NavLogo /> | ||
- {isLandingPage && ( | ||
- <span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Your Saas</span> | ||
- )} | ||
+ {isLandingPage && <span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Open Saas</span>} | ||
</Link> | ||
</div> | ||
<div className='flex lg:hidden'> | ||
@@ -60,9 +59,9 @@ | ||
</ul> | ||
{isUserLoading ? null : !user ? ( | ||
<Link to='/login' className='text-sm font-semibold leading-6 ml-3'> | ||
- <div className='flex items-center duration-300 ease-in-out text-gray-900 hover:text-yellow-500 dark:text-white'> | ||
- Log in <BiLogIn size='1.1rem' className='ml-1 mt-[0.1rem]' /> | ||
- </div> | ||
+ <div className='flex justify-end items-center duration-300 ease-in-out text-gray-900 hover:text-yellow-500 dark:text-white test-sm'> | ||
+ Try the demo App <BiLogIn size='1.1rem' className='ml-1' /> | ||
+ </div> | ||
</Link> | ||
) : ( | ||
<div className='ml-3'> | ||
@@ -95,7 +94,7 @@ | ||
{isUserLoading ? null : !user ? ( | ||
<Link to='/login'> | ||
<div className='flex justify-end items-center duration-300 ease-in-out text-gray-900 hover:text-yellow-500 dark:text-white'> | ||
- Log in <BiLogIn size='1.1rem' className='ml-1' /> | ||
+ Try the Demo App{' '} <BiLogIn size='1.1rem' className='ml-1' /> | ||
</div> | ||
</Link> | ||
) : ( | ||
@@ -137,3 +136,27 @@ | ||
); | ||
}); | ||
} | ||
+ | ||
+const ContestURL = | ||
+ 'https://docs.opensaas.sh/blog/'; | ||
+ | ||
+function Announcement() { | ||
+ return ( | ||
+ <div className='flex justify-center items-center gap-3 p-3 w-full bg-gradient-to-r from-[#d946ef] to-[#fc0] font-semibold text-white text-center z-49'> | ||
+ <p onClick={() => window.open(ContestURL, '_blank')} className='hidden lg:block cursor-pointer hover:opacity-90 hover:drop-shadow'>🍪 THE MOST ANNOYING COOKIE BANNER EVER HACKATHON 🤬</p> | ||
+ <div className='hidden lg:block self-stretch w-0.5 bg-white'></div> | ||
+ <div | ||
+ onClick={() => window.open(ContestURL, '_blank')} | ||
+ className='hidden lg:block cursor-pointer rounded-full bg-neutral-700 px-2.5 py-1 text-xs hover:bg-neutral-600 tracking-wider' | ||
+ > | ||
+ Enter here and win prizes! → | ||
+ </div> | ||
+ <div | ||
+ onClick={() => window.open(ContestURL, '_blank')} | ||
+ className='lg:hidden cursor-pointer rounded-full bg-neutral-700 px-2.5 py-1 text-xs hover:bg-neutral-600 tracking-wider' | ||
+ > | ||
+ 🍪 The Most Annoying Cookie Banner Contest! 🤬 → | ||
+ </div> | ||
+ </div> | ||
+ ); | ||
+} | ||
\ No newline at end of file |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
import { useAuth } from 'wasp/client/auth'; | ||
import { updateCurrentUser } from 'wasp/client/operations'; | ||
import './Main.css'; | ||
import AppNavBar from './components/AppNavBar'; | ||
import NavBar from './components/NavBar/NavBar'; | ||
import { appNavigationItems } from './components/NavBar/contentSections'; | ||
import { landingPageNavigationItems } from '../landing-page/contentSections'; | ||
import CookieConsentBanner from './components/cookie-consent/Banner'; | ||
import { useMemo, useEffect, ReactNode } from 'react'; | ||
import { useMemo, useEffect } from 'react'; | ||
import { Outlet, useLocation } from 'react-router-dom'; | ||
import { useIsLandingPage } from './hooks/useIsLandingPage'; | ||
|
||
/** | ||
* use this component to wrap all child components | ||
|
@@ -13,9 +16,10 @@ import { Outlet, useLocation } from 'react-router-dom'; | |
export default function App() { | ||
const location = useLocation(); | ||
const { data: user } = useAuth(); | ||
const isLandingPage = useIsLandingPage(); | ||
|
||
const shouldDisplayAppNavBar = useMemo(() => { | ||
return location.pathname !== '/' && location.pathname !== '/login' && location.pathname !== '/signup'; | ||
return location.pathname !== '/login' && location.pathname !== '/signup'; | ||
}, [location]); | ||
|
||
const isAdminDashboard = useMemo(() => { | ||
|
@@ -49,7 +53,9 @@ export default function App() { | |
<Outlet /> | ||
) : ( | ||
<> | ||
{shouldDisplayAppNavBar && <AppNavBar />} | ||
{shouldDisplayAppNavBar && ( | ||
<NavBar navigation={isLandingPage ? landingPageNavigationItems : appNavigationItems} /> | ||
|
||
)} | ||
<div className='mx-auto max-w-7xl sm:px-6 lg:px-8'> | ||
<Outlet /> | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,37 +1,47 @@ | ||||||
import { Link, routes } from 'wasp/client/router'; | ||||||
import { Link } from 'react-router-dom'; | ||||||
import { useAuth } from 'wasp/client/auth'; | ||||||
import { useState } from 'react'; | ||||||
import { useState, Dispatch, SetStateAction } from 'react'; | ||||||
import { Dialog } from '@headlessui/react'; | ||||||
import { BiLogIn } from 'react-icons/bi'; | ||||||
import { AiFillCloseCircle } from 'react-icons/ai'; | ||||||
import { HiBars3 } from 'react-icons/hi2'; | ||||||
import logo from '../static/logo.png'; | ||||||
import DropdownUser from '../../user/DropdownUser'; | ||||||
import { UserMenuItems } from '../../user/UserMenuItems'; | ||||||
import { DocsUrl, BlogUrl } from '../../shared/common'; | ||||||
import DarkModeSwitcher from './DarkModeSwitcher'; | ||||||
import logo from '../../static/logo.png'; | ||||||
import DropdownUser from '../../../user/DropdownUser'; | ||||||
import { UserMenuItems } from '../../../user/UserMenuItems'; | ||||||
import DarkModeSwitcher from '../DarkModeSwitcher'; | ||||||
import { useIsLandingPage } from '../../hooks/useIsLandingPage'; | ||||||
import { cn } from '../../cn'; | ||||||
|
||||||
const navigation = [ | ||||||
{ name: 'AI Scheduler (Demo App)', href: routes.DemoAppRoute.build() }, | ||||||
{ name: 'File Upload (AWS S3)', href: routes.FileUploadRoute.build() }, | ||||||
{ name: 'Pricing', href: routes.PricingPageRoute.build() }, | ||||||
{ name: 'Documentation', href: DocsUrl }, | ||||||
{ name: 'Blog', href: BlogUrl }, | ||||||
]; | ||||||
interface NavigationItem { | ||||||
name: string; | ||||||
to: string; | ||||||
} | ||||||
|
||||||
const NavLogo = () => <img className='h-8 w-8' src={logo} alt='Your SaaS App' />; | ||||||
|
||||||
export default function AppNavBar() { | ||||||
export default function AppNavBar({ navigation }: { navigation: NavigationItem[] }) { | ||||||
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); | ||||||
const isLandingPage = useIsLandingPage(); | ||||||
|
||||||
const { data: user, isLoading: isUserLoading } = useAuth(); | ||||||
return ( | ||||||
<header className='absolute inset-x-0 top-0 z-50 shadow sticky bg-white bg-opacity-50 backdrop-blur-lg backdrop-filter dark:border dark:border-gray-100/10 dark:bg-boxdark-2'> | ||||||
<header | ||||||
className={cn('absolute inset-x-0 top-0 z-50 dark:bg-boxdark-2', { | ||||||
'shadow sticky bg-white bg-opacity-50 backdrop-blur-lg backdrop-filter dark:border dark:border-gray-100/10': | ||||||
!isLandingPage, | ||||||
})} | ||||||
> | ||||||
<nav className='flex items-center justify-between p-6 lg:px-8' aria-label='Global'> | ||||||
<div className='flex lg:flex-1'> | ||||||
<a href='/' className='-m-1.5 p-1.5'> | ||||||
<img className='h-8 w-8' src={logo} alt='My SaaS App' /> | ||||||
</a> | ||||||
<div className='flex items-center lg:flex-1'> | ||||||
<Link | ||||||
to='/' | ||||||
className='flex items-center -m-1.5 p-1.5 text-gray-900 duration-300 ease-in-out hover:text-yellow-500' | ||||||
> | ||||||
<NavLogo /> | ||||||
{isLandingPage && ( | ||||||
<span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Your Saas</span> | ||||||
)} | ||||||
</Link> | ||||||
</div> | ||||||
<div className='flex lg:hidden'> | ||||||
<button | ||||||
|
@@ -43,30 +53,19 @@ export default function AppNavBar() { | |||||
<HiBars3 className='h-6 w-6' aria-hidden='true' /> | ||||||
</button> | ||||||
</div> | ||||||
<div className='hidden lg:flex lg:gap-x-12'> | ||||||
{navigation.map((item) => ( | ||||||
<a | ||||||
key={item.name} | ||||||
href={item.href} | ||||||
className='text-sm font-semibold leading-6 text-gray-900 duration-300 ease-in-out hover:text-yellow-500 dark:text-white' | ||||||
> | ||||||
{item.name} | ||||||
</a> | ||||||
))} | ||||||
</div> | ||||||
<div className='hidden lg:flex lg:gap-x-12'>{renderNavigationItems(navigation)}</div> | ||||||
<div className='hidden lg:flex lg:flex-1 gap-3 justify-end items-center'> | ||||||
<ul className='flex justify-center items-center gap-2 sm:gap-4'> | ||||||
<DarkModeSwitcher /> | ||||||
</ul> | ||||||
|
||||||
{isUserLoading ? null : !user ? ( | ||||||
<a href={!user ? routes.LoginRoute.build() : routes.AccountRoute.build()} className='text-sm font-semibold leading-6 ml-4'> | ||||||
<Link to='/login' className='text-sm font-semibold leading-6 ml-3'> | ||||||
<div className='flex items-center duration-300 ease-in-out text-gray-900 hover:text-yellow-500 dark:text-white'> | ||||||
Log in <BiLogIn size='1.1rem' className='ml-1 mt-[0.1rem]' /> | ||||||
</div> | ||||||
</a> | ||||||
</Link> | ||||||
) : ( | ||||||
<div className='ml-4'> | ||||||
<div className='ml-3'> | ||||||
<DropdownUser user={user} /> | ||||||
</div> | ||||||
)} | ||||||
|
@@ -76,10 +75,10 @@ export default function AppNavBar() { | |||||
<div className='fixed inset-0 z-50' /> | ||||||
<Dialog.Panel className='fixed inset-y-0 right-0 z-50 w-full overflow-y-auto bg-white dark:text-white dark:bg-boxdark px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-900/10'> | ||||||
<div className='flex items-center justify-between'> | ||||||
<a href='/' className='-m-1.5 p-1.5'> | ||||||
<Link to='/' className='-m-1.5 p-1.5'> | ||||||
<span className='sr-only'>Your SaaS</span> | ||||||
<NavLogo /> | ||||||
</a> | ||||||
</Link> | ||||||
<button | ||||||
type='button' | ||||||
className='-m-2.5 rounded-md p-2.5 text-gray-700 dark:text-gray-50' | ||||||
|
@@ -91,18 +90,7 @@ export default function AppNavBar() { | |||||
</div> | ||||||
<div className='mt-6 flow-root'> | ||||||
<div className='-my-6 divide-y divide-gray-500/10'> | ||||||
<div className='space-y-2 py-6'> | ||||||
{navigation.map((item) => ( | ||||||
<a | ||||||
key={item.name} | ||||||
href={item.href} | ||||||
onClick={() => setMobileMenuOpen(false)} | ||||||
className='-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50 dark:text-white hover:dark:bg-boxdark-2' | ||||||
> | ||||||
{item.name} | ||||||
</a> | ||||||
))} | ||||||
</div> | ||||||
<div className='space-y-2 py-6'>{renderNavigationItems(navigation, setMobileMenuOpen)}</div> | ||||||
<div className='py-6'> | ||||||
{isUserLoading ? null : !user ? ( | ||||||
<Link to='/login'> | ||||||
|
@@ -124,3 +112,28 @@ export default function AppNavBar() { | |||||
</header> | ||||||
); | ||||||
} | ||||||
|
||||||
function renderNavigationItems( | ||||||
navigation: NavigationItem[], | ||||||
|
navigation: NavigationItem[], | |
navigationItems: NavigationItem[], |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd maybe import both Link
from wasp/client/router
and react-router-dom
and use them in different places.
- I'd use the
import { Link as ReactRouterLink } from 'react-router-dom';
and<ReactRouterLink>
component in places where we are having dynamicto
prop (in this fn) - I'd use the Wasp's
Link
elsewhere where we use theto
prop explicitly - we get as much out of the type safe component as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid hard-coding values, you could use
routes.LoginRoute.build()
androutes.SignupRoute.build()
to get the routes :)