Skip to content
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

feature(website): merge existing webpages into /legal page #738

Merged
merged 14 commits into from
Feb 8, 2024
5 changes: 1 addition & 4 deletions shared/locales/de/website-common.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,13 @@
"recipients": "Empfänger:innen",
"reporting": "Dokumente",
"team": "Team",
"terms-and-conditions": "Teilnahmebedingungen",
"terms-of-use": "Nutzungsbedingungen",
"transparency": "Transparenz",
"whats-next": "Was als Nächstes kommt"
},
"footer": {
"follow-us": "Folge uns",
"resources": "Informationen",
"our-work": "Was wir tun",
"about-us": "Wer wir sind"
"legal": "Rechtliches"
},
"cookie-consent-banner": {
"text": "Dürfen wir Cookies benützen um den Traffic und die Leistung unserer Website zu analysieren? Wir sammeln niemals persönliche Daten. <a href='/privacy' class='underline'>Datenschutzrichtlinie</a>.",
Expand Down
14 changes: 14 additions & 0 deletions shared/locales/de/website-fundraisers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"metadata": {
"title": "Spendenaktionen | Social Income"
},
"title": "Spendenaktionen",
"guidelines": {
"title": "Richtlinien und Verantwortlichkeiten",
"section-1": "Die Person, die eine private Spendenaktion auf der Website von Social Income startet, ist für die Erstellung und Durchführung der Spendenaktion verantwortlich. Dies umfasst die Übernahme sämtlicher Kosten, die Planung, die Förderung und das Sicherstellen, dass geltende Gesetze eingehalten werden.",
"section-2": "Social Income wird auf seiner Webseite eine spezielle Seite für Spendenaktion einrichten. Jede Spendenaktion erhält einen eindeutigen Link und sollte alle Unterstützer anweisen, über diesen Link zu spenden. Die Person, welche die Spendenaktion startet, darf keine anderen direkten Spenden oder Zahlungen im Namen von Social Income annehmen.",
"section-3": "Die Person, die eine Spendenaktion startet, darf den Namen und das Logo von Social Income verwenden, um für die Spendenaktion zu werben und Spenden für Social Income zu sammeln. Dies bedeutet jedoch nicht, dass Social Income die Veranstaltung unterstützt. Social Income behält sich das Recht vor, die Verwendung seines Namens und Logos jederzeit einzustellen oder abzulehnen.",
"section-4": "Die Person, die eine Spendenaktion startet, willigt ein, dass Social Income Eigentümer aller gesammelten Daten ist, einschliesslich der während der Spendenaktion oder auf der Spendenseite gesammelten Informationen. Social Income kann es den Personen, die eine Spendeaktionen durchführen, erlauben, spezifische Daten zu verwenden, um den Spender:innen zu danken.",
"section-5": "Die Person, die eine Spendenaktion startet, ist dafür verantwortlich, sicherzustellen, dass alle Aspekte der Spendenaktion angemessen und respektvoll sind. Kein Teil der Veranstaltung sollte schädlich, beleidigend, unwahr oder diskriminierend sein. Social Income hat das Recht, die Spendenaktion jederzeit nach eigenem Ermessen zu beenden."
}
}
7 changes: 7 additions & 0 deletions shared/locales/de/website-legal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"title": "Rechtliche Infos",
"privacy-title": "Datenschutzrichtlinie",
"terms-of-use-title": "Nutzungsbedingungen",
"terms-and-conditions-title": "Teilnahmebedingungen",
"fundraisers-title": "Spendenaktionen"
}
5 changes: 1 addition & 4 deletions shared/locales/en/website-common.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,13 @@
"recipients": "Recipients",
"reporting": "Reporting",
"team": "Team",
"terms-and-conditions": "Terms and Conditions",
"terms-of-use": "Terms of Use",
"transparency": "Transparency",
"whats-next": "What's Next"
},
"footer": {
"follow-us": "Follow Us",
"resources": "Resources",
"our-work": "Our Work",
"about-us": "About Us"
"legal": "Legal"
},
"cookie-consent-banner": {
"text": "Do you allow us to use cookies for analyzing our website traffic and performance? We never collect any personal data. <a href='/privacy' class='underline'>Privacy Policy</a>.",
Expand Down
14 changes: 14 additions & 0 deletions shared/locales/en/website-fundraisers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"metadata": {
"title": "Fundraisers | Social Income"
},
"title": "Fundraisers",
"guidelines": {
"title": "Guidelines and Responsibilities",
"section-1": "The person starting a private fundraiser on Social Income’s website is responsible for setting up and running the fundraising event. This responsibility includes covering all costs, planning, promoting, and ensuring compliance with the law.",
"section-2": "Social Income will create a special ‘fundraising page’ on its website. The fundraiser will receive a unique link to this page and should direct all supporters to donate to Social Income through this specific page. The fundraiser must not accept directly donations or any other payments on behalf of Social Income.",
"section-3": "The fundraiser is permitted to use Social Income’s name and logo to promote the fundraising event and raise money for Social Income. However, this does not imply that Social Income endorses the event. Social Income reserves the right to discontinue or refuse the use of its name and logo at any time.",
"section-4": "The fundraiser understands that Social Income owns all data collected, including information gathered during the fundraiser or on the ‘fundraising page.’ Social Income may allow the fundraiser to use this data to thank donors.",
"section-5": "The fundraiser is responsible for ensuring that all aspects of the fundraising event are appropriate and respectful. No part of the event should be harmful, offensive, untrue, or discriminatory. Social Income has the right to terminate the fundraising campaign at any time at their discretion."
}
}
7 changes: 7 additions & 0 deletions shared/locales/en/website-legal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"title": "Legal information",
"privacy-title": "Privacy Policy",
"terms-of-use-title": "Terms of Use",
"terms-and-conditions-title": "Terms & Conditions",
"fundraisers-title": "Fundraisers"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { DefaultPageProps } from '@/app/[lang]/[region]';
import { Translator } from '@socialincome/shared/src/utils/i18n';
import { BaseContainer, Typography } from '@socialincome/ui';

export async function generateMetadata({ params }: DefaultPageProps) {
const translator = await Translator.getInstance({
language: params.lang,
namespaces: ['website-terms-and-conditions'],
});
return { title: translator.t('metadata.title') };
}

export default async function Page({ params }: DefaultPageProps) {
const translator = await Translator.getInstance({
language: params.lang,
namespaces: ['website-terms-and-conditions'],
});

return (
<BaseContainer className="mx-auto flex max-w-2xl flex-col space-y-12 py-8">
<Typography size="5xl" weight="bold">
{translator.t('title')}
</Typography>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('general.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('general.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('general.section-2') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('general.section-3') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('participation.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('participation.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('participation.section-2') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('participation.section-3') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('participation.section-4') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('participation.section-5') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('participation.section-6') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('data.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('data.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('data.section-2') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('data.section-3') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('liability.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('liability.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('liability.section-2') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('warranty.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('warranty.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('warranty.section-2') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('warranty.section-3') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('stopping.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('stopping.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('stopping.section-2') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('deletion.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('deletion.section-1') }} />
</div>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('disagreements.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('disagreements.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('disagreements.section-2') }} />
</div>
</BaseContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { DefaultPageProps } from '@/app/[lang]/[region]';
import { Translator } from '@socialincome/shared/src/utils/i18n';
import { BaseContainer, Typography } from '@socialincome/ui';

export async function generateMetadata({ params }: DefaultPageProps) {
const translator = await Translator.getInstance({
language: params.lang,
namespaces: ['website-fundraisers'],
});
return { title: translator.t('metadata.title') };
}
export default async function Page({ params }: DefaultPageProps) {
const translator = await Translator.getInstance({
language: params.lang,
namespaces: ['website-fundraisers'],
});

return (
<BaseContainer className="mx-auto flex max-w-2xl flex-col space-y-12 py-8">
<Typography size="5xl" weight="bold">
{translator.t('title')}
</Typography>
<div className="prose">
<Typography as="h3" size="3xl" weight="bold">
{translator.t('guidelines.title')}
</Typography>
<Typography dangerouslySetInnerHTML={{ __html: translator.t('guidelines.section-1') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('guidelines.section-2') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('guidelines.section-3') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('guidelines.section-4') }} />
<Typography dangerouslySetInnerHTML={{ __html: translator.t('guidelines.section-5') }} />
</div>
</BaseContainer>
);
}
122 changes: 122 additions & 0 deletions website/src/app/[lang]/[region]/(website)/legal/layout-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use client';

import { DefaultParams } from '@/app/[lang]/[region]';
import { DocumentTextIcon } from '@heroicons/react/24/outline';
import { Button, Collapsible, CollapsibleContent, CollapsibleTrigger, Typography } from '@socialincome/ui';
import { LinkProps } from 'next/dist/client/link';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import * as React from 'react';
import { PropsWithChildren, useState } from 'react';

type NavigationLinkProps = {
href: string;
Icon: React.ForwardRefExoticComponent<React.PropsWithoutRef<React.SVGProps<SVGSVGElement>>>;
} & LinkProps;

function NavigationLink({ href, Icon, children, ...props }: PropsWithChildren<NavigationLinkProps>) {
return (
<Link href={href} {...props}>
<li className="hover:bg-muted flex-inline flex items-center rounded px-4 py-3">
<Icon className="mr-2 h-5 w-5" />
<Typography>{children}</Typography>
</li>
</Link>
);
}

function NavigationSectionTitle({ children }: PropsWithChildren) {
return (
<Typography weight="medium" className="py-2">
{children}
</Typography>
);
}

type LayoutClientProps = {
params: DefaultParams;
translations: {
title: string;
privacyTitle: string;
termsOfUseTitle: string;
termsAndConditionsTitle: string;
fundraisersTitle: string;
};
};

export function LayoutClient({ params, translations, children }: PropsWithChildren<LayoutClientProps>) {
const pathname = usePathname();
const [isOpen, setIsOpen] = useState(false);

const navigationMenu = (
<ul className="pr-4">
<NavigationSectionTitle>{translations.title}</NavigationSectionTitle>
<NavigationLink
href={`/${params.lang}/${params.region}/legal/privacy`}
Icon={DocumentTextIcon}
onClick={() => setIsOpen(false)}
>
{translations.privacyTitle}
</NavigationLink>
<NavigationLink
href={`/${params.lang}/${params.region}/legal/site-use`}
Icon={DocumentTextIcon}
onClick={() => setIsOpen(false)}
>
{translations.termsOfUseTitle}
</NavigationLink>
<NavigationLink
href={`/${params.lang}/${params.region}/legal/donations`}
Icon={DocumentTextIcon}
onClick={() => setIsOpen(false)}
>
{translations.termsAndConditionsTitle}
</NavigationLink>
<NavigationLink
href={`/${params.lang}/${params.region}/legal/fundraisers`}
Icon={DocumentTextIcon}
onClick={() => setIsOpen(false)}
>
{translations.fundraisersTitle}
</NavigationLink>
</ul>
);

let title;
switch (pathname) {
case `/${params.lang}/${params.region}/legal/privacy`:
title = translations.privacyTitle;
break;
case `/${params.lang}/${params.region}/legal/site-use`:
title = translations.termsOfUseTitle;
break;
case `/${params.lang}/${params.region}/legal/donations`:
title = translations.termsAndConditionsTitle;
break;
case `/${params.lang}/${params.region}/legal/fundraisers`:
title = translations.fundraisersTitle;
break;
}

return (
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="grid grid-cols-1 md:grid-cols-4 lg:grid-cols-5">
<div className="hidden pt-16 md:col-span-1 md:flex lg:col-span-1">{navigationMenu}</div>
<div className="mt-4 md:col-span-3 lg:col-span-4">
<CollapsibleTrigger asChild>
<div className="flex justify-end md:hidden">
<Button
variant="ghost"
size="sm"
className="inline-flex items-center text-sm underline"
onClick={() => setIsOpen((isOpen) => !isOpen)}
>
{isOpen ? 'Hide Menu' : 'Show Menu'}
</Button>
</div>
</CollapsibleTrigger>
<CollapsibleContent className="-mt-10 mb-12 border-b md:hidden">{isOpen && navigationMenu}</CollapsibleContent>
{children}
</div>
</Collapsible>
);
}
31 changes: 31 additions & 0 deletions website/src/app/[lang]/[region]/(website)/legal/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { DefaultLayoutProps } from '@/app/[lang]/[region]';
import { LayoutClient } from '@/app/[lang]/[region]/(website)/legal/layout-client';
import { getMetadata } from '@/metadata';
import { Translator } from '@socialincome/shared/src/utils/i18n';
import { BaseContainer } from '@socialincome/ui';
import { PropsWithChildren } from 'react';

export async function generateMetadata({ params }: DefaultLayoutProps) {
return getMetadata(params.lang, 'website-legal');
}

export default async function Layout({ children, params }: PropsWithChildren<DefaultLayoutProps>) {
const translator = await Translator.getInstance({ language: params.lang, namespaces: 'website-legal' });

return (
<BaseContainer className="pb-8">
<LayoutClient
params={params}
translations={{
title: translator.t('title'),
privacyTitle: translator.t('privacy-title'),
termsOfUseTitle: translator.t('terms-of-use-title'),
termsAndConditionsTitle: translator.t('terms-and-conditions-title'),
fundraisersTitle: translator.t('fundraisers-title'),
}}
>
{children}
</LayoutClient>
</BaseContainer>
);
}
10 changes: 10 additions & 0 deletions website/src/app/[lang]/[region]/(website)/legal/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';

import { redirect } from 'next/navigation';
import { useEffect } from 'react';

export default function Page() {
useEffect(() => {
redirect('./legal/privacy');
}, []);
}
Loading
Loading