Update error handling and UI design across multiple files
Enhanced error handling in documentation and blog pages, to ensure smoother running and user experience. This also includes additional UI updates related to font selection, layout arrangement, and interactive elements on error pages, marketing pages, and general site navigation components. Moreover, a "contact us" feature has been added to error pages to help users seek assistance more conveniently.
This commit is contained in:
@@ -7,7 +7,7 @@ import appConfig from '~/config/app.config';
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer className={'mt-auto py-8 2xl:py-16 relative w-full site-footer'}>
|
||||
<footer className={'site-footer relative mt-auto w-full py-8 2xl:py-16'}>
|
||||
<div className={'container'}>
|
||||
<div className={'flex flex-col space-y-8 lg:flex-row lg:space-y-0'}>
|
||||
<div
|
||||
|
||||
@@ -9,7 +9,7 @@ export function SiteHeader(props: { user?: User | null }) {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'sticky top-0 z-10 w-full bg-background/80 backdrop-blur-md dark:bg-background/50 py-2 site-header'
|
||||
'site-header sticky top-0 z-10 w-full bg-background/80 py-2 backdrop-blur-md dark:bg-background/50'
|
||||
}
|
||||
>
|
||||
<div className={'container'}>
|
||||
|
||||
@@ -12,8 +12,7 @@ const getClassName = (path: string, currentPathName: string) => {
|
||||
return cn(
|
||||
`text-sm font-medium px-2.5 py-2 border rounded-lg border-transparent transition-colors duration-100`,
|
||||
{
|
||||
'dark:text-gray-300 dark:hover:text-white':
|
||||
!isActive,
|
||||
'dark:text-gray-300 dark:hover:text-white': !isActive,
|
||||
'dark:text-white text-current': isActive,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ const links = {
|
||||
Contact: {
|
||||
label: 'marketing:contact',
|
||||
path: '/contact',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export function SiteNavigation() {
|
||||
@@ -48,7 +48,11 @@ export function SiteNavigation() {
|
||||
return (
|
||||
<>
|
||||
<div className={'hidden items-center justify-center md:flex'}>
|
||||
<NavigationMenu className={'border border-gray-100 dark:border-primary/10 rounded-full py-2 px-4'}>
|
||||
<NavigationMenu
|
||||
className={
|
||||
'rounded-full border border-gray-100 px-4 py-2 dark:border-primary/10'
|
||||
}
|
||||
>
|
||||
<NavigationMenuList className={'space-x-1'}>
|
||||
{NavItems}
|
||||
</NavigationMenuList>
|
||||
|
||||
@@ -8,11 +8,19 @@ export function SitePageHeader(props: {
|
||||
return (
|
||||
<div className={cn('border-b py-8 xl:py-10 2xl:py-12', props.className)}>
|
||||
<div className={'container flex flex-col space-y-2 lg:space-y-4'}>
|
||||
<h1 className={'font-medium font-heading text-3xl xl:text-5xl dark:text-white tracking-tight'}>
|
||||
<h1
|
||||
className={
|
||||
'font-heading text-3xl font-medium tracking-tight dark:text-white xl:text-5xl'
|
||||
}
|
||||
>
|
||||
{props.title}
|
||||
</h1>
|
||||
|
||||
<h2 className={'text-lg text-muted-foreground 2xl:text-2xl tracking-tight'}>
|
||||
<h2
|
||||
className={
|
||||
'text-lg tracking-tight text-muted-foreground 2xl:text-2xl'
|
||||
}
|
||||
>
|
||||
{props.subtitle}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,11 @@ export function PostHeader({ post }: { post: Cms.ContentItem }) {
|
||||
<div className={'flex flex-1 flex-col'}>
|
||||
<div className={cn('border-b py-8')}>
|
||||
<div className={'mx-auto flex max-w-3xl flex-col space-y-4'}>
|
||||
<h1 className={'font-heading text-3xl font-semibold xl:text-5xl dark:text-white'}>
|
||||
<h1
|
||||
className={
|
||||
'font-heading text-3xl font-semibold dark:text-white xl:text-5xl'
|
||||
}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { createCmsClient } from '@kit/cms';
|
||||
import { getLogger } from '@kit/shared/logger';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
@@ -24,8 +25,10 @@ export const generateMetadata = async () => {
|
||||
const getContentItems = cache(
|
||||
async (language: string | undefined, limit: number, offset: number) => {
|
||||
const client = await createCmsClient();
|
||||
const logger = await getLogger();
|
||||
|
||||
return client.getContentItems({
|
||||
try {
|
||||
return await client.getContentItems({
|
||||
collection: 'posts',
|
||||
limit,
|
||||
offset,
|
||||
@@ -33,6 +36,11 @@ const getContentItems = cache(
|
||||
sortBy: 'publishedAt',
|
||||
sortDirection: 'desc',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({ error }, 'Failed to load blog posts');
|
||||
|
||||
return { total: 0, items: [] };
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { cache } from 'react';
|
||||
|
||||
import { createCmsClient } from '@kit/cms';
|
||||
import { getLogger } from '@kit/shared/logger';
|
||||
|
||||
/**
|
||||
* @name getDocs
|
||||
@@ -11,12 +12,19 @@ export const getDocs = cache(docsLoader);
|
||||
|
||||
async function docsLoader(language: string | undefined) {
|
||||
const cms = await createCmsClient();
|
||||
const logger = await getLogger();
|
||||
|
||||
const { items: pages } = await cms.getContentItems({
|
||||
try {
|
||||
const data = await cms.getContentItems({
|
||||
collection: 'documentation',
|
||||
language,
|
||||
limit: 500,
|
||||
});
|
||||
|
||||
return pages;
|
||||
return data.items;
|
||||
} catch (error) {
|
||||
logger.error({ error }, 'Failed to load documentation pages');
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ async function DocsLayout({ children }: React.PropsWithChildren) {
|
||||
const pages = await getDocs(resolvedLanguage);
|
||||
|
||||
return (
|
||||
<div className={'flex container'}>
|
||||
<div className={'container flex'}>
|
||||
<DocsNavigation pages={buildDocumentationTree(pages)} />
|
||||
|
||||
{children}
|
||||
|
||||
@@ -98,7 +98,9 @@ function Home() {
|
||||
|
||||
<Heading
|
||||
level={3}
|
||||
className={'font-sans font-normal text-muted-foreground tracking-normal'}
|
||||
className={
|
||||
'font-sans font-normal tracking-normal text-muted-foreground'
|
||||
}
|
||||
>
|
||||
Secure and Easy-to-Use Authentication for Your SaaS Website
|
||||
and API
|
||||
@@ -147,7 +149,9 @@ function Home() {
|
||||
|
||||
<Heading
|
||||
level={3}
|
||||
className={'font-sans font-normal text-muted-foreground tracking-normal'}
|
||||
className={
|
||||
'font-sans font-normal tracking-normal text-muted-foreground'
|
||||
}
|
||||
>
|
||||
A fantastic dashboard to manage your SaaS business
|
||||
</Heading>
|
||||
@@ -174,7 +178,9 @@ function Home() {
|
||||
|
||||
<Heading
|
||||
level={3}
|
||||
className={'font-sans font-normal text-muted-foreground tracking-normal'}
|
||||
className={
|
||||
'font-sans font-normal tracking-normal text-muted-foreground'
|
||||
}
|
||||
>
|
||||
A powerful billing system for your SaaS business
|
||||
</Heading>
|
||||
@@ -300,7 +306,7 @@ function MainCallToActionButton() {
|
||||
return (
|
||||
<div className={'flex space-x-4'}>
|
||||
<Button
|
||||
className={'h-12 px-4 rounded-xl text-base font-semibold'}
|
||||
className={'h-12 rounded-xl px-4 text-base font-semibold'}
|
||||
asChild
|
||||
>
|
||||
<Link href={'/auth/sign-up'}>
|
||||
@@ -321,7 +327,7 @@ function MainCallToActionButton() {
|
||||
|
||||
<Button
|
||||
variant={'link'}
|
||||
className={'h-12 px-4 rounded-xl text-base font-semibold'}
|
||||
className={'h-12 rounded-xl px-4 text-base font-semibold'}
|
||||
asChild
|
||||
>
|
||||
<Link href={'/contact'}>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ArrowLeft, MessageCircle } from 'lucide-react';
|
||||
|
||||
import { useCaptureException } from '@kit/monitoring/hooks';
|
||||
import { Button } from '@kit/ui/button';
|
||||
@@ -27,32 +29,44 @@ const ErrorPage = ({
|
||||
'container m-auto flex w-full flex-1 flex-col items-center justify-center'
|
||||
}
|
||||
>
|
||||
<div className={'flex flex-col items-center space-y-16'}>
|
||||
<div className={'flex flex-col items-center space-y-8'}>
|
||||
<div>
|
||||
<h1 className={'font-heading text-9xl font-extrabold'}>
|
||||
<h1 className={'font-heading text-9xl font-semibold'}>
|
||||
<Trans i18nKey={'common:errorPageHeading'} />
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className={'flex flex-col items-center space-y-8'}>
|
||||
<div className={'flex flex-col items-center space-y-2.5'}>
|
||||
<div
|
||||
className={
|
||||
'flex max-w-xl flex-col items-center space-y-1 text-center'
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Heading level={1}>
|
||||
<Heading level={2}>
|
||||
<Trans i18nKey={'common:genericError'} />
|
||||
</Heading>
|
||||
</div>
|
||||
|
||||
<p className={'text-muted-foreground'}>
|
||||
<p className={'text-lg text-muted-foreground'}>
|
||||
<Trans i18nKey={'common:genericErrorSubHeading'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button variant={'outline'} onClick={reset}>
|
||||
<div className={'flex space-x-4'}>
|
||||
<Button className={'w-full'} variant={'default'} onClick={reset}>
|
||||
<ArrowLeft className={'mr-2 h-4'} />
|
||||
|
||||
<Trans i18nKey={'common:goBack'} />
|
||||
</Button>
|
||||
|
||||
<Button className={'w-full'} variant={'outline'} asChild>
|
||||
<Link href={'/contact'}>
|
||||
<MessageCircle className={'mr-2 h-4'} />
|
||||
|
||||
<Trans i18nKey={'common:contactUs'} />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ArrowLeft, MessageCircle } from 'lucide-react';
|
||||
|
||||
import { useCaptureException } from '@kit/monitoring/hooks';
|
||||
import { Button } from '@kit/ui/button';
|
||||
@@ -29,32 +31,48 @@ const GlobalErrorPage = ({
|
||||
'container m-auto flex w-full flex-1 flex-col items-center justify-center'
|
||||
}
|
||||
>
|
||||
<div className={'flex flex-col items-center space-y-16'}>
|
||||
<div className={'flex flex-col items-center space-y-8'}>
|
||||
<div>
|
||||
<h1 className={'font-heading text-9xl font-extrabold'}>
|
||||
<h1 className={'font-heading text-9xl font-semibold'}>
|
||||
<Trans i18nKey={'common:errorPageHeading'} />
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className={'flex flex-col items-center space-y-8'}>
|
||||
<div className={'flex flex-col items-center space-y-2.5'}>
|
||||
<div
|
||||
className={
|
||||
'flex max-w-xl flex-col items-center space-y-1 text-center'
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Heading level={1}>
|
||||
<Heading level={2}>
|
||||
<Trans i18nKey={'common:genericError'} />
|
||||
</Heading>
|
||||
</div>
|
||||
|
||||
<p className={'text-muted-foreground'}>
|
||||
<p className={'text-lg text-muted-foreground'}>
|
||||
<Trans i18nKey={'common:genericErrorSubHeading'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button variant={'outline'} onClick={reset}>
|
||||
<Button
|
||||
className={'w-full'}
|
||||
variant={'default'}
|
||||
onClick={reset}
|
||||
>
|
||||
<ArrowLeft className={'mr-2 h-4'} />
|
||||
|
||||
<Trans i18nKey={'common:goBack'} />
|
||||
</Button>
|
||||
|
||||
<Button className={'w-full'} variant={'outline'} asChild>
|
||||
<Link href={'/contact'}>
|
||||
<MessageCircle className={'mr-2 h-4'} />
|
||||
|
||||
<Trans i18nKey={'common:contactUs'} />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,9 @@ async function getGitHash() {
|
||||
try {
|
||||
return await getHashFromProcess();
|
||||
} catch (error) {
|
||||
console.warn(`[WARN] Could not find git hash: ${JSON.stringify(error)}. You may want to provide a fallback.`);
|
||||
console.warn(
|
||||
`[WARN] Could not find git hash: ${JSON.stringify(error)}. You may want to provide a fallback.`,
|
||||
);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ function PricingItem(
|
||||
<Price>
|
||||
{lineItem
|
||||
? formatCurrency(props.product.currency, lineItem.cost)
|
||||
: props.plan.label ?? <Trans i18nKey={'billing:custom'} />}
|
||||
: (props.plan.label ?? <Trans i18nKey={'billing:custom'} />)}
|
||||
</Price>
|
||||
|
||||
<If condition={props.plan.name}>
|
||||
@@ -444,7 +444,7 @@ function DefaultCheckoutButton(
|
||||
<Link className={'w-full'} href={linkHref}>
|
||||
<Button
|
||||
size={'lg'}
|
||||
className={'border-primary w-full border rounded-lg'}
|
||||
className={'border-primary w-full rounded-lg border'}
|
||||
variant={props.highlighted ? 'default' : 'outline'}
|
||||
>
|
||||
<span>
|
||||
|
||||
@@ -27,7 +27,7 @@ export function EmailPasswordSignUpContainer({
|
||||
defaultValues,
|
||||
onSignUp,
|
||||
emailRedirectTo,
|
||||
displayTermsCheckbox
|
||||
displayTermsCheckbox,
|
||||
}: EmailPasswordSignUpContainerProps) {
|
||||
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
|
||||
|
||||
|
||||
@@ -28,7 +28,11 @@ export function createI18nSettings({
|
||||
lowerCaseLng: true as const,
|
||||
fallbackNS: ns,
|
||||
missingInterpolationHandler: (text, value, options) => {
|
||||
console.debug(`Missing interpolation value for key: ${text}`, value, options);
|
||||
console.debug(
|
||||
`Missing interpolation value for key: ${text}`,
|
||||
value,
|
||||
options,
|
||||
);
|
||||
},
|
||||
ns,
|
||||
react: {
|
||||
|
||||
@@ -12,7 +12,7 @@ export function Heading({
|
||||
return (
|
||||
<h1
|
||||
className={cn(
|
||||
`scroll-m-20 font-heading text-3xl lg:text-4xl font-bold tracking-tight dark:text-white`,
|
||||
`scroll-m-20 font-heading text-3xl font-bold tracking-tight dark:text-white lg:text-4xl`,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -23,7 +23,7 @@ export function Heading({
|
||||
return (
|
||||
<h2
|
||||
className={cn(
|
||||
`scroll-m-20 pb-2 font-heading text-2xl lg:text-3xl font-semibold tracking-tight transition-colors first:mt-0`,
|
||||
`scroll-m-20 pb-2 font-heading text-2xl font-semibold tracking-tight transition-colors first:mt-0 lg:text-3xl`,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -34,7 +34,7 @@ export function Heading({
|
||||
return (
|
||||
<h3
|
||||
className={cn(
|
||||
'scroll-m-20 font-heading text-xl lg:text-2xl font-semibold tracking-tight',
|
||||
'scroll-m-20 font-heading text-xl font-semibold tracking-tight lg:text-2xl',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -45,7 +45,7 @@ export function Heading({
|
||||
return (
|
||||
<h4
|
||||
className={cn(
|
||||
'scroll-m-20 font-heading text-lg lg:text-xl font-semibold tracking-tight',
|
||||
'scroll-m-20 font-heading text-lg font-semibold tracking-tight lg:text-xl',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -56,7 +56,7 @@ export function Heading({
|
||||
return (
|
||||
<h5
|
||||
className={cn(
|
||||
'scroll-m-20 font-heading text-base lg:text-lg font-medium',
|
||||
'scroll-m-20 font-heading text-base font-medium lg:text-lg',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user