Skip to content

Commit

Permalink
feat: add interface for error pages (#2589)
Browse files Browse the repository at this point in the history
* feat: add interface for error pages

* chore: remove console.log from error page

* chore: remove error message from error page

* refactor: error page

* fix: 404 and error pages styles

* fix: 404 and error pages buttons styles

* fix: 404 and error pages buttons styles

* fix: 404 and error pages buttons styles

* fix: restore missing header sidebar

* fix: missing darkTheme for GithubStars
  • Loading branch information
saimonkat authored Dec 18, 2024
1 parent 09b3045 commit 79e176f
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 68 deletions.
36 changes: 18 additions & 18 deletions src/app/(home-page)/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,24 @@ const jsonLd = {
url: 'https://neon.tech/',
};

const Homepage = () => {
return <>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<Hero />
<Logos />
<InstantProvisioning />
<Lightning />
<Bento />
<AiIndex />
<Multitenancy />
<Industry />
<Trusted />
<Cta />
</>
}
const Homepage = () => (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<Hero />
<Logos />
<InstantProvisioning />
<Lightning />
<Bento />
<AiIndex />
<Multitenancy />
<Industry />
<Trusted />
<Cta />
</>
);

export default Homepage;

Expand Down
40 changes: 40 additions & 0 deletions src/app/error.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import PropTypes from 'prop-types';
import { useEffect } from 'react';

import Hero from 'components/pages/error/hero';
import Layout from 'components/shared/layout';
import SEO_DATA from 'constants/seo-data';

/*
NOTE:
This page is needed to handle unexpected errors and display fallback UI.
*/
const ErrorPage = ({ error, reset }) => {
useEffect(() => {
// eslint-disable-next-line no-console
console.error(error);
}, [error]);

return (
<>
<title>{SEO_DATA.error.title}</title>

<Layout isClient>
<Hero
title="Page is broken..."
text="Sorry, the page you are looking for is broken. Please try again later, we'll fix it soon!"
reset={reset}
/>
</Layout>
</>
);
};

ErrorPage.propTypes = {
error: PropTypes.object,
reset: PropTypes.func,
};

export default ErrorPage;
7 changes: 5 additions & 2 deletions src/app/not-found.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Hero from 'components/pages/404/hero';
import Hero from 'components/pages/error/hero';
import Layout from 'components/shared/layout';
import SEO_DATA from 'constants/seo-data';

Expand All @@ -12,7 +12,10 @@ const NotFoundPage = () => (
<title>{SEO_DATA[404].title}</title>

<Layout>
<Hero />
<Hero
title="Page not found..."
text="Sorry, the page you are looking for doesn’t exist or has been moved."
/>
</Layout>
</>
);
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,36 @@ import Link from 'components/shared/link';

import illustration from './images/illustration.png';

const CTA = ({ isDocsPage = false }) =>
const CTA = ({ isDocsPage = false, reset }) =>
isDocsPage ? (
<div className="flex w-full flex-col">
<InkeepTrigger className="my-8 w-full" isNotFoundPage />
<Link className="mt-8 self-start" size="lg" theme="black-primary-1" to="/">
Back to home
<Link className="whitespace-nowrap" size="sm" theme="green" to="/" withArrow>
Back to Home
</Link>
</div>
) : (
<Button className="mt-11 self-start lg:mt-8 sm:w-full" size="md" theme="primary" to="/">
Back to Home
</Button>
<div className="mt-11 flex items-center gap-6 lg:mt-8 lg:gap-4 sm:mt-6">
{reset ? (
<>
<Button size="xs" theme="primary" withArrow onClick={reset}>
Try again
</Button>
<Link className="whitespace-nowrap" size="sm" theme="green" to="/" withArrow>
Back to Home
</Link>
</>
) : (
<Button size="xs" theme="primary" to="/">
Back to Home
</Button>
)}
</div>
);

CTA.propTypes = {
isDocsPage: PropTypes.bool,
reset: PropTypes.func,
};

const Skeleton = () => (
Expand All @@ -38,7 +52,7 @@ const Skeleton = () => (
</div>
);

const Hero = () => {
const Hero = ({ title, text, reset }) => {
const pathname = usePathname();
const [isDocsPage, setIsDocsPage] = useState(false);
const [isLoading, setIsLoading] = useState(true);
Expand All @@ -49,21 +63,22 @@ const Hero = () => {
}, [pathname]);

return (
<section className="grow pb-24 pt-16 dark:bg-black-pure dark:text-white lg:pt-0 md:py-14 xs:pt-10">
<Container className="grid grid-cols-12 items-start gap-x-8 md:gap-x-0 md:gap-y-4" size="md">
<div className="col-start-2 col-end-6 flex flex-col pt-48 2xl:col-start-1 xl:pt-20 lg:pt-10 md:col-span-full md:pt-0">
<h1 className="font-title text-[58px] font-medium leading-none xl:text-5xl xl:leading-none md:text-4xl">
<section className="flex grow flex-col pb-24 pt-16 dark:bg-black-pure dark:text-white lg:pt-0 md:py-14 xs:pt-10">
<Container
className="grid grow grid-cols-12 items-center gap-x-8 md:gap-x-0 md:gap-y-4"
size="md"
>
<div className="col-start-2 col-end-6 flex flex-col 2xl:col-start-1 lg:col-end-7 md:col-span-full">
<h1 className="font-title text-[58px] font-medium leading-none xl:text-5xl xl:leading-none lg:text-4xl">
Ooops!
<br /> Page not found...
<br />
{title}
</h1>
<p className="t-xl mt-7 lg:mt-8">
Sorry, the page you are looking for doesn’t exist or has been moved.
</p>

{isLoading ? <Skeleton /> : <CTA isDocsPage={isDocsPage} />}
<p className="t-xl mt-7 max-w-md sm:mt-4">{text}</p>
{isLoading ? <Skeleton /> : <CTA isDocsPage={isDocsPage} reset={reset} />}
</div>

<div className="col-start-6 col-end-12 2xl:col-end-13 md:col-span-full">
<div className="col-start-6 col-end-12 2xl:col-end-13 lg:col-start-7 md:col-span-full">
<Image
className="w-full md:mx-auto md:max-w-xl"
width={860}
Expand All @@ -79,4 +94,10 @@ const Hero = () => {
);
};

Hero.propTypes = {
title: PropTypes.string,
text: PropTypes.string,
reset: PropTypes.func,
};

export default Hero;
File renamed without changes.
68 changes: 40 additions & 28 deletions src/components/shared/header/header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const themePropTypes = {
isDarkTheme: PropTypes.bool,
};

const Navigation = async ({ isDarkTheme }) => (
const Navigation = ({ isDarkTheme }) => (
<nav>
<ul className="flex gap-x-10 xl:gap-x-8 lg:hidden [@media(max-width:1070px)]:gap-x-6">
{MENUS.header.map(({ to, text, items }, index) => {
Expand Down Expand Up @@ -156,36 +156,46 @@ const Navigation = async ({ isDarkTheme }) => (

Navigation.propTypes = themePropTypes;

const Sidebar = async ({ isDarkTheme }) => {
const GithubStars = async ({ isDarkTheme }) => {
const starsCount = await getGithubStars();
return (
<div className="flex items-center gap-x-6 lg:hidden">
<Suspense>
<GithubStarCounter isDarkTheme={isDarkTheme} starsCount={starsCount} />
</Suspense>
<Link
className="text-[13px] leading-none tracking-extra-tight lg:hidden"
to={LINKS.login}
theme={isDarkTheme ? 'white' : 'black'}
>
Log In
</Link>

<Button
className="h-8 px-6 text-[13px] font-semibold leading-none tracking-extra-tight transition-colors duration-200 lg:hidden"
to={LINKS.signup}
theme="primary"
tag_name="Header"
analyticsEvent="header_sign_up_clicked"
>
Sign Up
</Button>
</div>
<Suspense>
<GithubStarCounter isDarkTheme={isDarkTheme} starsCount={starsCount} />
</Suspense>
);
};
Sidebar.propTypes = themePropTypes;

const Header = async ({
GithubStars.propTypes = themePropTypes;

const Sidebar = ({ isDarkTheme, isClient }) => (
<div className="flex items-center gap-x-6 lg:hidden">
{!isClient && <GithubStars isDarkTheme={isDarkTheme} />}
<Link
className="text-[13px] leading-none tracking-extra-tight lg:hidden"
to={LINKS.login}
theme={isDarkTheme ? 'white' : 'black'}
>
Log In
</Link>

<Button
className="h-8 px-6 text-[13px] font-semibold leading-none tracking-extra-tight transition-colors duration-200 lg:hidden"
to={LINKS.signup}
theme="primary"
tag_name="Header"
analyticsEvent="header_sign_up_clicked"
>
Sign Up
</Button>
</div>
);

Sidebar.propTypes = {
...themePropTypes,
isClient: PropTypes.bool,
};

const Header = ({
className = null,
theme = null,
isSticky = false,
Expand All @@ -196,6 +206,7 @@ const Header = async ({
withBorder = false,
searchIndexName = null,
customType = null,
isClient = false,
}) => {
const isDarkTheme = theme === 'dark';

Expand Down Expand Up @@ -235,7 +246,7 @@ const Header = async ({
<InkeepTrigger className="w-[272px]" isPostgresPage={isPostgresPage} showAIButton />
</div>
<div className="col-span-2 col-start-11 -ml-12 h-full max-w-64 3xl:col-start-11 3xl:-ml-20 2xl:col-span-4 2xl:col-start-9 2xl:ml-6 xl:ml-0 lg:hidden">
<Sidebar />
<Sidebar isClient={isClient} />
</div>
</Container>
</div>
Expand All @@ -252,7 +263,7 @@ const Header = async ({
/>
<Navigation isDarkTheme={isDarkTheme} />
</div>
<Sidebar isDarkTheme={isDarkTheme} />
<Sidebar isDarkTheme={isDarkTheme} isClient={isClient} />
</Container>
)}
</HeaderWrapper>
Expand Down Expand Up @@ -280,6 +291,7 @@ Header.propTypes = {
title: PropTypes.string,
link: PropTypes.string,
}),
isClient: PropTypes.bool,
};

export default Header;
3 changes: 3 additions & 0 deletions src/components/shared/layout/layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const Layout = ({
isPostgresPage = false,
searchIndexName = null,
customType = null,
isClient = false,
}) => (
<>
<Topbar isDarkTheme={headerTheme === 'dark'} />
Expand All @@ -40,6 +41,7 @@ const Layout = ({
withBorder={headerWithBorder}
searchIndexName={searchIndexName}
customType={customType}
isClient={isClient}
/>
<main
className={clsx(withOverflowHidden && 'overflow-hidden', 'flex flex-1 flex-col', className)}
Expand Down Expand Up @@ -71,6 +73,7 @@ Layout.propTypes = {
title: PropTypes.string,
link: PropTypes.string,
}),
isClient: PropTypes.bool,
};

export default Layout;
2 changes: 1 addition & 1 deletion src/components/shared/topbar/topbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const TopBar = ({ isDarkTheme }) => (
isDarkTheme ? 'text-gray-new-90' : 'text-gray-new-15'
)}
>
Is your RDS bill bloated? Cut up to 75% in compute costs with autoscaling and scale-to-zero
Is your RDS bill bloated? Cut up to 75% in compute costs with autoscaling and scale-to-zero
</span>
<ChevronIcon
className={clsx(
Expand Down
3 changes: 3 additions & 0 deletions src/constants/seo-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ export default {
'Join us online on October 30th at 10:00 AM PT to learn how Neon empowers developers to ship faster with Postgres.',
pathname: LINKS.stage,
},
error: {
title: 'Page Is Broken — Neon',
},
404: {
title: 'Page Not Found — Neon',
},
Expand Down
2 changes: 1 addition & 1 deletion src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const generateEditPageURL = (handle) => `${SITE_URL}/tickets/${handle}/edit`;
export async function middleware(req) {
try {
const { pathname } = req.nextUrl;

try {
const isLoggedIn = await checkCookie('neon_login_indicator');
if (pathname === '/' && isLoggedIn) {
Expand Down

0 comments on commit 79e176f

Please sign in to comment.