Migrate privacy policy to Nextjs
This commit is contained in:
parent
b23c538408
commit
877c4b3479
|
|
@ -9,7 +9,8 @@
|
|||
"react/display-name": "off",
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"space-infix-ops": "warn",
|
||||
"comma-spacing": "warn"
|
||||
"comma-spacing": "warn",
|
||||
"react-hooks/rules-of-hooks": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Metadata } from 'next'
|
||||
|
||||
import Egg from '/src/components/Egg/Egg'
|
||||
import Footer from '/src/components/Footer/Footer'
|
||||
import Settings from '/src/components/Settings/Settings'
|
||||
import TranslateDialog from '/src/components/TranslateDialog/TranslateDialog'
|
||||
import { fallbackLng } from '/src/i18n/options'
|
||||
|
|
@ -10,7 +11,10 @@ import './global.css'
|
|||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL('https://crab.fit'),
|
||||
title: 'Crab Fit',
|
||||
title: {
|
||||
absolute: 'Crab Fit',
|
||||
template: '%s - Crab Fit',
|
||||
},
|
||||
keywords: ['crab', 'fit', 'crabfit', 'schedule', 'availability', 'availabilities', 'when2meet', 'doodle', 'meet', 'plan', 'time', 'timezone'],
|
||||
description: 'Enter your availability to find a time that works for everyone!',
|
||||
themeColor: '#F79E00',
|
||||
|
|
@ -32,9 +36,13 @@ const RootLayout = async ({ children }: { children: React.ReactNode }) => {
|
|||
return <html lang={resolvedLanguage ?? fallbackLng}>
|
||||
<body>
|
||||
<Settings />
|
||||
{children}
|
||||
<Egg />
|
||||
<TranslateDialog />
|
||||
|
||||
{children}
|
||||
|
||||
{/* @ts-expect-error Async Server Component */}
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import Link from 'next/link'
|
|||
import Content from '/src/components/Content/Content'
|
||||
import CreateForm from '/src/components/CreateForm/CreateForm'
|
||||
import DownloadButtons from '/src/components/DownloadButtons/DownloadButtons'
|
||||
import Footer from '/src/components/Footer/Footer'
|
||||
import Header from '/src/components/Header/Header'
|
||||
import { default as P } from '/src/components/Paragraph/Paragraph'
|
||||
import { P } from '/src/components/Paragraph/Text'
|
||||
import Recents from '/src/components/Recents/Recents'
|
||||
import Section from '/src/components/Section/Section'
|
||||
import Stats from '/src/components/Stats/Stats'
|
||||
|
|
@ -47,9 +46,6 @@ const Page = async () => {
|
|||
<P>{t('about.content.p5')}</P>
|
||||
</Content>
|
||||
</Section>
|
||||
|
||||
{/* @ts-expect-error Async Server Component */}
|
||||
<Footer />
|
||||
</>
|
||||
}
|
||||
|
||||
|
|
|
|||
29
frontend/src/app/privacy/GoogleTranslate.tsx
Normal file
29
frontend/src/app/privacy/GoogleTranslate.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const TRANSLATION_DISCLAIMER = 'While the translated document is provided for your convenience, the English version as displayed at https://crab.fit is legally binding.'
|
||||
|
||||
interface GoogleTranslateProps {
|
||||
children: React.ReactNode
|
||||
language: string
|
||||
}
|
||||
|
||||
// Show a link to translate the privacy policy to the user's preferred language
|
||||
const GoogleTranslate = ({ language, children }: GoogleTranslateProps) => {
|
||||
const [content, setContent] = useState<string>()
|
||||
|
||||
useEffect(() => {
|
||||
setContent(document.querySelector<HTMLDivElement>('#policy')?.innerText)
|
||||
}, [])
|
||||
|
||||
return content ? <p>
|
||||
<a
|
||||
href={`https://translate.google.com/?sl=en&tl=${language.substring(0, 2)}&text=${encodeURIComponent(`${TRANSLATION_DISCLAIMER}\n\n${content}`)}&op=translate`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>{children}</a>
|
||||
</p> : null
|
||||
}
|
||||
|
||||
export default GoogleTranslate
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
import { styled } from 'goober'
|
||||
|
||||
export const Note = styled('p')`
|
||||
.note {
|
||||
background-color: var(--surface);
|
||||
border: 1px solid var(--primary);
|
||||
border-radius: 10px;
|
||||
|
|
@ -13,10 +11,4 @@ export const Note = styled('p')`
|
|||
& a {
|
||||
color: var(--secondary);
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonArea = styled('div')`
|
||||
@media print {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
|
@ -1,46 +1,37 @@
|
|||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Metadata } from 'next'
|
||||
|
||||
import { Button, Center, Footer, Logo } from '/src/components'
|
||||
import GoogleTranslate from '/src/app/privacy/GoogleTranslate'
|
||||
import Button from '/src/components/Button/Button'
|
||||
import Content from '/src/components/Content/Content'
|
||||
import Header from '/src/components/Header/Header'
|
||||
import { P, Ul } from '/src/components/Paragraph/Text'
|
||||
import Section from '/src/components/Section/Section'
|
||||
import { useTranslation } from '/src/i18n/server'
|
||||
|
||||
import { StyledMain, AboutSection, P } from '../Home/Home.styles'
|
||||
import { Note, ButtonArea } from './Privacy.styles'
|
||||
import styles from './page.module.scss'
|
||||
|
||||
const translationDisclaimer = 'While the translated document is provided for your convenience, the English version as displayed at https://crab.fit is legally binding.'
|
||||
export const generateMetadata = async (): Promise<Metadata> => {
|
||||
const { t } = await useTranslation('privacy')
|
||||
|
||||
const Privacy = () => {
|
||||
const navigate = useNavigate()
|
||||
const { t, i18n } = useTranslation(['common', 'privacy'])
|
||||
const contentRef = useRef()
|
||||
const [content, setContent] = useState('')
|
||||
return {
|
||||
title: t('name'),
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${t('privacy:name')} - Crab Fit`
|
||||
}, [t])
|
||||
|
||||
useEffect(() => setContent(contentRef.current?.innerText || ''), [contentRef])
|
||||
const Page = async () => {
|
||||
const { t, i18n } = await useTranslation(['common', 'privacy'])
|
||||
|
||||
return <>
|
||||
<StyledMain>
|
||||
<Logo />
|
||||
</StyledMain>
|
||||
<Content>
|
||||
{/* @ts-expect-error Async Server Component */}
|
||||
<Header />
|
||||
|
||||
<StyledMain>
|
||||
<h1>{t('privacy:name')}</h1>
|
||||
|
||||
{!i18n.language.startsWith('en') && (
|
||||
<p>
|
||||
<a
|
||||
href={`https://translate.google.com/?sl=en&tl=${i18n.language.substring(0, 2)}&text=${encodeURIComponent(`${translationDisclaimer}\n\n${content}`)}&op=translate`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>{t('privacy:translate')}</a>
|
||||
</p>
|
||||
)}
|
||||
{!i18n.language.startsWith('en') && <GoogleTranslate language={i18n.language}>{t('privacy:translate')}</GoogleTranslate>}
|
||||
|
||||
<h3>Crab Fit</h3>
|
||||
<div ref={contentRef}>
|
||||
<div id="policy">
|
||||
<P>This SERVICE is provided by Benjamin Grant at no cost and is intended for use as is.</P>
|
||||
<P>This page is used to inform visitors regarding the policies of the collection, use, and disclosure of Personal Information if using the Service.</P>
|
||||
<P>If you choose to use the Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that is collected is used for providing and improving the Service. Your information will not be used or shared with anyone except as described in this Privacy Policy.</P>
|
||||
|
|
@ -48,9 +39,9 @@ const Privacy = () => {
|
|||
<h2>Information Collection and Use</h2>
|
||||
<P>The Service uses third party services that may collect information used to identify you.</P>
|
||||
<P>Links to privacy policies of the third party service providers used by the Service:</P>
|
||||
<P as="ul">
|
||||
<Ul>
|
||||
<li><a href="https://www.google.com/policies/privacy/" target="blank">Google Play Services</a></li>
|
||||
</P>
|
||||
</Ul>
|
||||
|
||||
<h2>Log Data</h2>
|
||||
<P>When you use the Service, in the case of an error, data and information is collected to improve the Service, which may include your IP address, device name, operating system version, app configuration and the time and date of the error.</P>
|
||||
|
|
@ -61,17 +52,17 @@ const Privacy = () => {
|
|||
|
||||
<h2>Service Providers</h2>
|
||||
<P>Third-party companies may be employed for the following reasons:</P>
|
||||
<P as="ul">
|
||||
<Ul>
|
||||
<li>To facilitate the Service</li>
|
||||
<li>To provide the Service on our behalf</li>
|
||||
<li>To perform Service-related services</li>
|
||||
<li>To assist in analyzing how the Service is used</li>
|
||||
</P>
|
||||
</Ul>
|
||||
<P>To perform these tasks, the third parties may have access to your Personal Information, but are obligated not to disclose or use this information for any purpose except the above.</P>
|
||||
|
||||
<h2>Security</h2>
|
||||
<P>Personal Information that is shared via the Service is protected, however remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, so take care when sharing Personal Information.</P>
|
||||
<Note>Events that are created will be automatically permanently erased from storage after <strong>3 months</strong> of inactivity.</Note>
|
||||
<p className={styles.note}>Events that are created will be automatically permanently erased from storage after <strong>3 months</strong> of inactivity.</p>
|
||||
|
||||
<h2>Links to Other Sites</h2>
|
||||
<P>The Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by the Service. Therefore, you are advised to review the Privacy Policy of these websites.</P>
|
||||
|
|
@ -86,18 +77,14 @@ const Privacy = () => {
|
|||
<h2>Contact Us</h2>
|
||||
<P>If you have any questions or suggestions about the Privacy Policy, do not hesitate to contact us at <a href="mailto:contact@crab.fit">contact@crab.fit</a>.</P>
|
||||
</div>
|
||||
</StyledMain>
|
||||
</Content>
|
||||
|
||||
<ButtonArea>
|
||||
<AboutSection>
|
||||
<StyledMain>
|
||||
<Center><Button onClick={() => navigate('/')}>{t('common:cta')}</Button></Center>
|
||||
</StyledMain>
|
||||
</AboutSection>
|
||||
</ButtonArea>
|
||||
|
||||
<Footer />
|
||||
<Section>
|
||||
<Content isCentered>
|
||||
<Button href="/">{t('common:cta')}</Button>
|
||||
</Content>
|
||||
</Section>
|
||||
</>
|
||||
}
|
||||
|
||||
export default Privacy
|
||||
export default Page
|
||||
|
|
@ -3,3 +3,9 @@
|
|||
margin: 20px auto;
|
||||
max-width: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.centered {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { makeClass } from '/src/utils'
|
||||
|
||||
import styles from './Content.module.scss'
|
||||
|
||||
interface ContentProps {
|
||||
children: React.ReactNode
|
||||
isCentered?: boolean
|
||||
}
|
||||
|
||||
const Content = (props: ContentProps) =>
|
||||
<div className={styles.content} {...props} />
|
||||
const Content = ({ isCentered, ...props }: ContentProps) =>
|
||||
<div className={makeClass(styles.content, isCentered && styles.centered)} {...props} />
|
||||
|
||||
export default Content
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
import styles from './Paragraph.module.scss'
|
||||
|
||||
interface ParagraphProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const Paragraph = (props: ParagraphProps) =>
|
||||
<p className={styles.p} {...props} />
|
||||
|
||||
export default Paragraph
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.p {
|
||||
.text {
|
||||
font-weight: 500;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
11
frontend/src/components/Paragraph/Text.tsx
Normal file
11
frontend/src/components/Paragraph/Text.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import styles from './Text.module.scss'
|
||||
|
||||
interface TextProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const P = (props: TextProps) =>
|
||||
<p className={styles.text} {...props} />
|
||||
|
||||
export const Ul = (props: TextProps) =>
|
||||
<ul className={styles.text} {...props} />
|
||||
|
|
@ -2,8 +2,4 @@
|
|||
margin: 30px 0 0;
|
||||
background-color: var(--surface);
|
||||
padding: 20px 0;
|
||||
|
||||
& a {
|
||||
color: var(--secondary);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue