feat: add schema.org metadata
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { CardDescription } from '@components/Card';
|
import { CardDescription } from '@components/Card';
|
||||||
import CustomCard from '@components/CustomCard';
|
import CustomCard from '@components/CustomCard';
|
||||||
|
import Schema from '@components/SchemaOrg';
|
||||||
import { ResponseType } from '@utils/validationSchemas';
|
import { ResponseType } from '@utils/validationSchemas';
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { Suspense, useEffect, useState } from 'react';
|
import { Suspense, useEffect, useState } from 'react';
|
||||||
@@ -54,7 +56,7 @@ function ConfirmationPage() {
|
|||||||
fetchData();
|
fetchData();
|
||||||
}, [code, router]);
|
}, [code, router]);
|
||||||
|
|
||||||
function render() {
|
const renderContent = () => {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
return (
|
return (
|
||||||
<CardDescription className='text-center'>{message}</CardDescription>
|
<CardDescription className='text-center'>{message}</CardDescription>
|
||||||
@@ -62,22 +64,33 @@ function ConfirmationPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 'Just a second...';
|
return 'Just a second...';
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomCard
|
<CustomCard
|
||||||
className='max-90vw w-96'
|
className='max-90vw w-96'
|
||||||
title={loading ? 'Verifying' : 'Confirmed!'}
|
title={loading ? 'Verifying' : 'Confirmed!'}
|
||||||
content={render()}
|
content={renderContent()}
|
||||||
footer={false}
|
footer={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Confirmation() {
|
export default function Confirmation() {
|
||||||
|
const schema = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'WebSite',
|
||||||
|
name: 'HackerNews Newsletter',
|
||||||
|
title: 'Subscription Confirmation',
|
||||||
|
url: `${process.env.HOME_URL}/confirmation`
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<>Loading...</>}>
|
<>
|
||||||
<ConfirmationPage />
|
<Schema schema={schema} />
|
||||||
</Suspense>
|
<Suspense fallback={<>Loading...</>}>
|
||||||
|
<ConfirmationPage />
|
||||||
|
</Suspense>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
29
app/page.tsx
29
app/page.tsx
@@ -1,4 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@components/Button';
|
import { Button } from '@components/Button';
|
||||||
import { CardDescription } from '@components/Card';
|
import { CardDescription } from '@components/Card';
|
||||||
import CustomCard from '@components/CustomCard';
|
import CustomCard from '@components/CustomCard';
|
||||||
@@ -6,6 +7,7 @@ import ErrorMessage from '@components/ErrorMessage';
|
|||||||
import { FormControl } from '@components/form/FormControl';
|
import { FormControl } from '@components/form/FormControl';
|
||||||
import { FormMessage } from '@components/form/FormMessage';
|
import { FormMessage } from '@components/form/FormMessage';
|
||||||
import { Input } from '@components/Input';
|
import { Input } from '@components/Input';
|
||||||
|
import Schema from '@components/SchemaOrg';
|
||||||
import { FormField } from '@contexts/FormField/FormFieldProvider';
|
import { FormField } from '@contexts/FormField/FormFieldProvider';
|
||||||
import { FormItem } from '@contexts/FormItem/FormItemProvider';
|
import { FormItem } from '@contexts/FormItem/FormItemProvider';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
@@ -22,6 +24,14 @@ export default function Home() {
|
|||||||
const [message, setMessage] = useState('');
|
const [message, setMessage] = useState('');
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'WebSite',
|
||||||
|
name: 'HackerNews Newsletter',
|
||||||
|
title: 'Home',
|
||||||
|
url: process.env.HOME_URL
|
||||||
|
};
|
||||||
|
|
||||||
const form = useForm<SubscribeFormType>({
|
const form = useForm<SubscribeFormType>({
|
||||||
resolver: zodResolver(SubscribeFormSchema),
|
resolver: zodResolver(SubscribeFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -58,7 +68,7 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
const renderContent = () => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return ErrorMessage();
|
return ErrorMessage();
|
||||||
}
|
}
|
||||||
@@ -105,14 +115,17 @@ export default function Home() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomCard
|
<>
|
||||||
className='max-90vw w-96'
|
<Schema schema={schema} />
|
||||||
title='Interested in keeping up with the latest from the tech world? 👩💻'
|
<CustomCard
|
||||||
description='Subscribe to our newsletter! The top stories from Hackernews for you. Once a day. Every day.'
|
className='max-90vw w-96'
|
||||||
content={render()}
|
title='Interested in keeping up with the latest from the tech world? 👩💻'
|
||||||
/>
|
description='Subscribe to our newsletter! The top stories from Hackernews for you. Once a day. Every day.'
|
||||||
|
content={renderContent()}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import CustomCard from '@components/CustomCard';
|
import CustomCard from '@components/CustomCard';
|
||||||
|
import Schema from '@components/SchemaOrg';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export default function Privacy() {
|
export default function Privacy() {
|
||||||
|
const schema = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'WebSite',
|
||||||
|
name: 'HackerNews Newsletter',
|
||||||
|
title: 'Privacy Policy',
|
||||||
|
url: `${process.env.HOME_URL}/privacy`
|
||||||
|
};
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<div className='my-2 max-h-[60vh] overflow-auto'>
|
<div className='my-2 max-h-[60vh] overflow-auto'>
|
||||||
<p>
|
<p>
|
||||||
@@ -439,11 +448,14 @@ export default function Privacy() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomCard
|
<>
|
||||||
className='max-90vh max-90vw'
|
<Schema schema={schema} />
|
||||||
title='Privacy Policy'
|
<CustomCard
|
||||||
description='Last updated: December 03, 2023'
|
className='max-90vh max-90vw'
|
||||||
content={body}
|
title='Privacy Policy'
|
||||||
/>
|
description='Last updated: December 03, 2023'
|
||||||
|
content={body}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@components/Button';
|
import { Button } from '@components/Button';
|
||||||
import { CardDescription } from '@components/Card';
|
import { CardDescription } from '@components/Card';
|
||||||
import CustomCard from '@components/CustomCard';
|
import CustomCard from '@components/CustomCard';
|
||||||
@@ -6,6 +7,7 @@ import ErrorMessage from '@components/ErrorMessage';
|
|||||||
import { FormControl } from '@components/form/FormControl';
|
import { FormControl } from '@components/form/FormControl';
|
||||||
import { FormMessage } from '@components/form/FormMessage';
|
import { FormMessage } from '@components/form/FormMessage';
|
||||||
import { Input } from '@components/Input';
|
import { Input } from '@components/Input';
|
||||||
|
import Schema from '@components/SchemaOrg';
|
||||||
import { FormField } from '@contexts/FormField/FormFieldProvider';
|
import { FormField } from '@contexts/FormField/FormFieldProvider';
|
||||||
import { FormItem } from '@contexts/FormItem/FormItemProvider';
|
import { FormItem } from '@contexts/FormItem/FormItemProvider';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
@@ -23,6 +25,14 @@ export default function Unsubscribe() {
|
|||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const ref = useRef<HTMLInputElement | null>(null);
|
const ref = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'WebSite',
|
||||||
|
name: 'HackerNews Newsletter',
|
||||||
|
title: 'Unsubscribe',
|
||||||
|
url: `${process.env.HOME_URL}/unsubscribe`
|
||||||
|
};
|
||||||
|
|
||||||
const form = useForm<UnsubscribeFormType>({
|
const form = useForm<UnsubscribeFormType>({
|
||||||
resolver: zodResolver(UnsubscribeFormSchema),
|
resolver: zodResolver(UnsubscribeFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -65,7 +75,7 @@ export default function Unsubscribe() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
const renderContent = () => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return ErrorMessage();
|
return ErrorMessage();
|
||||||
}
|
}
|
||||||
@@ -108,14 +118,17 @@ export default function Unsubscribe() {
|
|||||||
</FormProvider>
|
</FormProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomCard
|
<>
|
||||||
className='max-90vw w-96'
|
<Schema schema={schema} />
|
||||||
title='Unsubscribe'
|
<CustomCard
|
||||||
description='You sure you want to leave? :('
|
className='max-90vw w-96'
|
||||||
content={render()}
|
title='Unsubscribe'
|
||||||
/>
|
description='You sure you want to leave? :('
|
||||||
|
content={renderContent()}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
13
components/SchemaOrg.tsx
Normal file
13
components/SchemaOrg.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import Head from 'next/head';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const SchemaOrg = ({ schema }: Record<string, any>) => (
|
||||||
|
<Head>
|
||||||
|
<script
|
||||||
|
type='application/ld+json'
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default SchemaOrg;
|
||||||
Reference in New Issue
Block a user