diff --git a/public/locales/en/landing.json b/public/locales/en/landing.json index 8689f2afd..4aa68109c 100644 --- a/public/locales/en/landing.json +++ b/public/locales/en/landing.json @@ -55,6 +55,204 @@ } } }, + "TERMS": { + "TITLE": "Terms of service", + "INTRODUCTION": "By using the Graasp platform and its software, you:", + "TERM_1": "Confirm that you will not misuse Graasp, or help anyone else to do so", + "TERM_2": "Confirm that you will not use Graasp for illicit purposes", + "TERM_3": "Confirm that you are solely responsible for the datasets and results handled or generated by Graasp", + "TERM_4": "Accept that the Graasp Association and École Polytechnique Fédérale de Lausanne (EPFL) cannot be held liable for any damage resulting from your use of Graasp, including any loss of content and data", + "TERM_5": "Accept that access to Graasp is provided to you ‘as is’, without any warranty, including as to its proper functioning and fitness for a particular purpose", + "FINAL_NOTE": "These Terms of Service are governed by the laws of Switzerland. The legal jurisdiction for any dispute shall be Valais (VS), Switzerland.", + "REFERENCES": "References", + "GRAASP_LEGAL_DISCLAIMER": { + "TEXT": "Graasp Legal disclaimer" + }, + "EPFL_LEGAL_DISCLAIMER": { + "TEXT": "EPFL Websites disclaimer" + } + }, + "DISCLAIMER": { + "TITLE": "Disclaimer", + "TEXT": "Graasp (the ‘software’) and its source code are freely distributed under the GNU Affero General Public License (AGPLv3). The software and source code are provided to you “as is”, without warranty of any kind, express or implied. Without limiting the generality of the foregoing, the Graasp Association and École Polytechnique Fédérale de Lausanne (EPFL) make no representation or warranty regarding the functionality, reliability, or performance of the software, or the results to be obtained through the use of the software, or that the operation of the software shall be error-free. In no event shall the Graasp Association or EPFL be liable for any damages arising from the use of Graasp, including but not limited to the distribution, misuse, leakage, or theft of any original files or datasets or duplicates thereof handled, processed, or generated by the software." + }, + "PRIVACY_POLICY": { + "TITLE": "Privacy Policy", + "SECTION_1": { + "TITLE": "Who we are and how to contact us", + "DEFINITION": "Graasp is developed and maintained by the Graasp Association and supported by the École Polytechnique Fédérale de Lausanne (EPFL), a public university of science and technology in Switzerland.", + "COMPLIANCE": "Graasp complies with the privacy policies of EPFL, the Swiss Federal Act on Data Protection (FADP), and with the European GDPR.", + "CONTACT": "For any questions related to this Privacy Policy you can contact Graasp: {{email}}" + }, + "RESPONSIBILITIES": { + "TITLE": "Responsibilities", + "USER_OBLIGATIONS": { + "TITLE": "User’s obligations", + "TEXT": "As user, you agree:", + "AGREEMENT_1": "that you bear all responsibility for the Uploaded Data (see Section 4.4) in Graasp. Graasp does not check users’ uploaded/created content for appropriateness, violations of privacy rights or Intellectual Property Rights. You are responsible for the content that you upload, because you are the data manager over your Uploaded Data;", + "AGREEMENT_2": "that you will comply at all times with relevant professional and business secrecy obligations if applicable;", + "AGREEMENT_3": "that Graasp only acts as a data manager regarding the storage and use of your Uploaded Data. Graasp only provides storage spaces and central processing units;", + "AGREEMENT_4": "that you are aware of the technical and organizational security measures implemented in Graasp;", + "AGREEMENT_5": "that you will promptly notify Graasp about:", + "NOTIFICATION": { + "CASE_1": "any legally binding request for disclosure of the Personal data by a law enforcement authority unless otherwise prohibited;", + "CASE_2": "any accidental or unauthorized access to proprietary, Personal or Uploaded Data, if technical assistance is required.", + "CASE_3": "to deal promptly and properly with all inquiries relating to the processing of the Personal Data as requested by applicable laws." + } + }, + "GRAASP_OBLIGATIONS": { + "TITLE": "Obligations of Graasp", + "TEXT": "Graasp agrees and warrants to provide the services according to the Terms of Use. Graasp is acting as a data manager and warrants:", + "AGREEMENT_1": "that Graasp keeps detailed and updated records of all management activities carried out on the Personal and Uploaded Data;", + "AGREEMENT_2": "to guarantee the confidentiality of your Personal and Uploaded Data if they are configured as private;", + "AGREEMENT_3": "promptly comply with any request from users requiring Graasp to amend, transfer, delete, or otherwise dispose of the Personal and Uploaded Data, or to cease, mitigate, or remedy any authorized processing;", + "AGREEMENT_4": "that Graasp will not process Personal or Uploaded Data for Graasp‘s own commercial benefit or that of any third party;", + "AGREEMENT_5": "notify the users without undue delay of any suspected or actual data breach involving their Personal or Uploaded Data;", + "AGREEMENT_6": "to notify the user immediately if Graasp receives any complaint, request, or other communication concerning the processing of the Personal or Uploaded Data.", + "AGREEMENT_7": "to assist users with appropriate measures and by providing information for the fulfillment of your obligations under the applicable law." + } + }, + "DATA_USAGE": { + "TITLE": "How Graasp uses your Personal and Uploaded Data", + "ACCOUNT": { + "TITLE": "Manage your account", + "TEXT": "Graasp processes your Account Data (see Section 4.1) for the purposes of operating the Graasp platform and software, providing the Graasp services, and communicating with you about Graasp. The legal basis for the Account Data processing is the contract between users and Graasp as stated in the Terms of Use." + }, + "PROVIDE_SERVICES": { + "TITLE": "Provide our services", + "TEXT": "Graasp processes your Meta-data (see Section 4.2) to provide the Graasp services. For instance, it is used to describe the source of the Personal and Uploaded Data, the lineage, and to display activities performed on the Personal and Uploaded Data. The legal basis for the Meta-data management is the contract between users and Graasp as stated in the Terms of Use." + }, + "ANALYZE_USAGE": { + "TITLE": " Ensure the functionalities and understand the use of Graasp", + "TEXT": "Graasp processes Operational Data (see Section 4.3) for the purpose of analyzing the use of Graasp, but also to ensure the functionalities of Graasp and the comfort of use. The legal basis for the Operational Data processing is the contract between users and Graasp as stated in the Terms of Use." + }, + "COMPLIANCE": { + "TITLE": "Compliance with the law", + "TEXT": "Graasp may process Personal and Uploaded Data, in case of suspected or actual criminal or administrative proceedings led by competent authorities (see Section 4.4). The legal basis for this processing is necessary to carry out a legal obligation." + }, + "RESEARCH": { + "TITLE": "Research purposes", + "TEXT": "Graasp may collect and use your Personal and Upload Data for research purposes. Published scientific results only contain anonymized data." + } + }, + "DATA_COLLECTION": { + "TITLE": "Data collected", + "TEXT": "Graasp may collect all the following Personal data (referred to as the « Personal Data »).", + "ACCOUNT": { + "TITLE": "Account Data", + "TEXT": "In order to manage the account of a user, Graasp needs to process the following Personal Data (« Account Data »):", + "DATA_1": "Email address", + "DATA_2": "Full name", + "DATA_3": "User ID", + "DATA_4": "Password", + "DATA_5": "Other log information", + "DATA_6": "IP address", + "DATA_7": "Timestamps of access to Graasp", + "DATA_8": "Consent logs" + }, + "METADATA": { + "TITLE": "Meta-data", + "TEXT": "In order to provide Graasp services, the information that is collected from your activities in Graasp is required. For instance, collecting provenance of the data (where this data comes from, how it was created) and its lineage (who is using the data and how). Provenance information is not collected automatically without the user’s knowledge, its creation and subsequent collection is subject to a User’s deliberate action using Graasp." + }, + "OPERATIONAL_DATA": { + "TITLE": "Operational Data", + "TEXT": "In order to ensure the operational functioning of Graasp and to understand its use, Graasp needs to process the following Personal Data (« Operational Data »):", + "DATA_1": "User IP addresses", + "DATA_2": "User ID", + "DATA_3": "Telemetry data, i.e. system data, used to get a pulse of the system, how hardware resources are used, by whom and understand why things fail, so that the Graasp team can troubleshoot them, or provide user support", + "DATA_4": "Logs from the firewall service", + "DATA_5": "Other logs required to ensure the functionalities of Graasp", + "DATA_6": "The number and duration of your visits", + "DATA_7": "Information about what parts of Graasp you visited" + }, + "UPLOADED_DATA": { + "TITLE": "Uploaded Data", + "TEXT": "You can create content (“Uploaded Data”) which may include Personal Data (e.g. educational data, digital documents, etc). You have the control over this data, as explained in the Terms of Use. As mentioned in the Terms of Use, users can decide to make public the Uploaded Data. In this case, other users may use your Uploaded Data for their own purpose, and Graasp cannot be liable for their further-use. Graasp hereby declines all responsibility for the further-use of your Uploaded Data by third-parties." + } + }, + "COOKIES": { + "TITLE": "Cookies", + "DEFINITION": "A cookie is a file containing an identifier (a string of letters and numbers) that is sent by a web server to a web browser and is stored by the browser. When you access the Graasp platform, the installation of cookies on your devices is possible according to your settings, which will allow Graasp to recognize your browser, during the validity time of the cookies.", + "PURPOSE": { + "TEXT": "The cookies that Graasp generates allows:", + "PURPOSE_1": "to prepare statistics including frequency of access, the use and performances of websites;", + "PURPOSE_2": "to store preferences and parameters (authentification)", + "PURPOSE_3": "to enable you to access reserved or personal content on the Graasp platform", + "PURPOSE_4": "to improve our communication and services." + }, + "MANAGEMENT": { + "TEXT": "Most web browsers automatically accept cookies but provide controls that allow you to block or delete them. Please refer to your browser’s privacy or help documentation to find instructions for blocking or deleting cookies. For example:", + "EDGE": "Edge", + "SAFARI": "Safari", + "CHROME": "Chrome", + "FIREFOX": "Firefox" + }, + "FINALITY": { + "TEXT": "If you choose to refuse cookies, you may not have access to certain functionalities of Graasp, which we cannot be held responsible for. The identifier is then sent back to the server each time the browser requests a page from the server. Cookies or similar technologies can be useful in many ways to make your visit in Graasp simpler, more pleasant and more pertinent. Various types of cookies are present in Graasp:", + "NECESSARY": "Cookies that are strictly necessary, which are necessary for navigation, along with your authentication token after logging in, and to secure Graasp", + "FUNCTIONAL": "Functionality cookies, which are collecting some of your surfing preferences such as language preferences", + "ADVERTISING": "Graasp does not use advertising cookies" + } + }, + "DATA_RETENTION": { + "TITLE": "Data retention", + "TEXT": "Graasp stores your Personal and Uploaded Data as long as your account exists. In order to fulfill our legal obligations or to fulfill our purpose described in Section 4.3, the Operational Data may be anonymized and/or may be kept for a longer period.", + "STORAGE_LOCATION": "Your Personal Data is stored on secure servers at our Service Provider’s premises in continental Europe (Germany and Switzerland)." + }, + "DATA_SHARING": { + "TITLE": "Data Sharing", + "TEXT": "Graasp is sharing your Personal Data with the following recipients exclusively:", + "SHARE_1": "Authorized employees and Graasp managers", + "SHARE_2": "Our Service Providers" + }, + "SERVICE_PROVIDERS": { + "TITLE": "Service Providers and Transfers", + "AWS": { + "TITLE": "Amazon Web Services (Main service provider)", + "TEXT": "Graasp uses the services of AWS (Amazon Web Services). No international transfers are foreseen, but their privacy framework can be consulted at the link below.", + "PRIVACY_POLICY_LINK": "AWS Privacy Policy" + }, + "OTHER_PROVIDERS": { + "TITLE": "Other service providers", + "GOOGLE_ANALYTICS": "Graasp uses anonymised Google Analytics services and Google ReCaptcha services to secure and understand usage of the platform.", + "SENTRY": "Anonymised data is also shared with Sentry.io for debugging and troubleshooting purposes in order to ensure a smooth and stable experience to our users.", + "GOOGLE_FONTS": "Graasp may use the services of Google Fonts and other javascript libraries hosted on third-party servers. In those cases, only IP addresses would be communicated to those service providers." + }, + "TRANSFERS": { + "TITLE": "Transfers", + "TEXT": "All your Personal and Uploaded Data is hosted by AWS, in Continental Europe (Germany and Switzerland). Copy of the data might be temporarily stored in the Graasp premises for quality insurance purposes." + } + }, + "SECURITY": { + "TITLE": "Security", + "TEXT": "Graasp has put in place appropriate security measures pursuant to the acknowledged state of the art. Please note however that Graasp cannot guarantee an absolute security for your Personal and Uploaded Data, to the extent that the data retention and electronic transmission involves certain risks." + }, + "YOUR_RIGHTS": { + "TITLE": "Your Rights", + "TEXT": "The user has a number of rights according to the data protection legislation. These rights can be limited in particular when they affect rights and freedom of others. Graasp will inform you of applicable exceptions in our answer to your potential request.", + "RIGHTS": { + "TEXT": "These rights include:", + "RIGHT_1": "right of access: You have the right to know what Personal Data Graasp hold about you and to ask, in writing, to see your Personal Data. You can directly have this information by contacting the data controllers: contact@graasp.org", + "RIGHT_2": "right to be informed: You have the right to be informed how your Personal and Uploaded Data will be used. This Privacy Policy as well as any additional information or notice that is provided to you either at the time you provided your details, or otherwise, is intended to provide you with this information.", + "RIGHT_3": "right to withdraw consent: Graasp processes your Personal Data and Uploaded Data on the basis of your consent; you can withdraw that consent at any time.", + "RIGHT_4": "right of erasure: In some cases, you have the right to have your Personal and Uploaded Data to be deleted.", + "RIGHT_5": "right of rectification: If you believe your Personal Data is inaccurate you have the right to ask for their update.", + "RIGHT_6": "right to file a complaint: If you are unhappy with the way in which Graasp have handled your Personal or Uploaded Data, you have the right to file a complaint with the Federal Data Protection and Information Commissioner (FDPIC) or with the supervisory authority of your country or residency." + }, + "RESERVATION": "Graasp reserve the right to refuse any abusive request or one which is contrary to applicable laws." + }, + "POLICY_CHANGES": { + "TITLE": "Changes to this Privacy Policy", + "TEXT": "We may revise this Privacy Policy from time to time. The most current version of the policy will govern our processing of your Personal and Uploaded Data and will always be at https://graasp.org/privacy. In case of modifications, they will be published on https://graasp.org/. If you disagree with any of the changes to the Privacy Policy, you must stop using Graasp and ask for the deletion of your account." + }, + "FINAL_PROVISIONS": { + "TITLE": "Final provisions", + "APPLICABLE_LAW": { + "TITLE": "Applicable law and jurisdiction", + "TEXT": "Swiss material laws are applicable. The place of execution and of jurisdiction is in Valais." + } + } + }, "FOOTER": { "TAG_LINE": "Developed in Switzerland by the Graasp Association", "CONTENT": { diff --git a/src/config/constants.ts b/src/config/constants.ts index 462c259df..060df4e6b 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -25,3 +25,5 @@ export const NS = { Common: 'common', Enums: 'enums', } as const; + +export const PRIVACY_EMAIL = 'privacy@graasp.org'; diff --git a/src/modules/landing/footer/Footer.tsx b/src/modules/landing/footer/Footer.tsx index 1529cbb75..9f398b145 100644 --- a/src/modules/landing/footer/Footer.tsx +++ b/src/modules/landing/footer/Footer.tsx @@ -81,7 +81,17 @@ const socialLinks = [ }, ]; -const internalLinkActiveProp = () => ({ sx: { textDecoration: 'underline' } }); +const internalLinkActiveProp = () => ({ + sx: { + backgroundColor: '#00000040', + '&::before': { + content: `url("data:image/svg+xml,${encodeURI('')}")`, + position: 'relative', + top: '2px', + marginRight: '8px', + }, + }, +}); export function Footer(): JSX.Element { const { t } = useTranslation(NS.Landing); diff --git a/src/modules/landing/home/icons/BeLEARN.tsx b/src/modules/landing/home/icons/BeLEARN.tsx index 345d07647..67f816ba4 100644 --- a/src/modules/landing/home/icons/BeLEARN.tsx +++ b/src/modules/landing/home/icons/BeLEARN.tsx @@ -17,23 +17,7 @@ export function BeLEARN({ BeLEARN + {title} + + {children} + + + ); +} + +export function SectionTitle({ + children, +}: { + children: ReactNode; +}): JSX.Element { + return ( + + {children} + + ); +} + +export function ListItem({ children }: { children: ReactNode }): JSX.Element { + return {children}; +} +export function Paragraphs({ children }: { children: ReactNode }): JSX.Element { + return ( + + {children} + + ); +} + +export function SubSection({ + children, + title, +}: { + children: ReactNode; + title: string; +}): JSX.Element { + return ( + + + {title} + + {children} + + ); +} + +export function EnumeratedParagraph({ + children, + text, +}: { + children: ReactNode; + text: ReactNode; +}): JSX.Element { + return ( + + {text} + + {children} + + + ); +} + +export function ListedParagraph({ + children, + text, +}: { + children: ReactNode; + text?: ReactNode; +}): JSX.Element { + return ( + + {text && {text}} + + {children} + + + ); +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 4a9e67fff..71dde020d 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -13,15 +13,9 @@ import { createFileRoute } from '@tanstack/react-router' // Import Routes import { Route as rootRoute } from './routes/__root' -import { Route as TermsImport } from './routes/terms' -import { Route as SupportImport } from './routes/support' -import { Route as PolicyImport } from './routes/policy' -import { Route as FeaturesImport } from './routes/features' -import { Route as DisclaimerImport } from './routes/disclaimer' -import { Route as ContactUsImport } from './routes/contact-us' import { Route as AuthImport } from './routes/auth' import { Route as AccountImport } from './routes/account' -import { Route as AboutUsImport } from './routes/about-us' +import { Route as LandingImport } from './routes/_landing' import { Route as AccountIndexImport } from './routes/account/index' import { Route as EmailChangeImport } from './routes/email.change' import { Route as AuthSuccessImport } from './routes/auth/success' @@ -31,6 +25,13 @@ import { Route as AuthLoginImport } from './routes/auth/login' import { Route as AuthForgotPasswordImport } from './routes/auth/forgot-password' import { Route as AccountStorageImport } from './routes/account/storage' import { Route as AccountSettingsImport } from './routes/account/settings' +import { Route as LandingTermsImport } from './routes/_landing/terms' +import { Route as LandingSupportImport } from './routes/_landing/support' +import { Route as LandingPolicyImport } from './routes/_landing/policy' +import { Route as LandingFeaturesImport } from './routes/_landing/features' +import { Route as LandingDisclaimerImport } from './routes/_landing/disclaimer' +import { Route as LandingContactUsImport } from './routes/_landing/contact-us' +import { Route as LandingAboutUsImport } from './routes/_landing/about-us' // Create Virtual Routes @@ -38,42 +39,6 @@ const IndexLazyImport = createFileRoute('/')() // Create/Update Routes -const TermsRoute = TermsImport.update({ - id: '/terms', - path: '/terms', - getParentRoute: () => rootRoute, -} as any) - -const SupportRoute = SupportImport.update({ - id: '/support', - path: '/support', - getParentRoute: () => rootRoute, -} as any) - -const PolicyRoute = PolicyImport.update({ - id: '/policy', - path: '/policy', - getParentRoute: () => rootRoute, -} as any) - -const FeaturesRoute = FeaturesImport.update({ - id: '/features', - path: '/features', - getParentRoute: () => rootRoute, -} as any) - -const DisclaimerRoute = DisclaimerImport.update({ - id: '/disclaimer', - path: '/disclaimer', - getParentRoute: () => rootRoute, -} as any) - -const ContactUsRoute = ContactUsImport.update({ - id: '/contact-us', - path: '/contact-us', - getParentRoute: () => rootRoute, -} as any) - const AuthRoute = AuthImport.update({ id: '/auth', path: '/auth', @@ -86,9 +51,8 @@ const AccountRoute = AccountImport.update({ getParentRoute: () => rootRoute, } as any) -const AboutUsRoute = AboutUsImport.update({ - id: '/about-us', - path: '/about-us', +const LandingRoute = LandingImport.update({ + id: '/_landing', getParentRoute: () => rootRoute, } as any) @@ -152,6 +116,48 @@ const AccountSettingsRoute = AccountSettingsImport.update({ getParentRoute: () => AccountRoute, } as any) +const LandingTermsRoute = LandingTermsImport.update({ + id: '/terms', + path: '/terms', + getParentRoute: () => LandingRoute, +} as any) + +const LandingSupportRoute = LandingSupportImport.update({ + id: '/support', + path: '/support', + getParentRoute: () => LandingRoute, +} as any) + +const LandingPolicyRoute = LandingPolicyImport.update({ + id: '/policy', + path: '/policy', + getParentRoute: () => LandingRoute, +} as any) + +const LandingFeaturesRoute = LandingFeaturesImport.update({ + id: '/features', + path: '/features', + getParentRoute: () => LandingRoute, +} as any) + +const LandingDisclaimerRoute = LandingDisclaimerImport.update({ + id: '/disclaimer', + path: '/disclaimer', + getParentRoute: () => LandingRoute, +} as any) + +const LandingContactUsRoute = LandingContactUsImport.update({ + id: '/contact-us', + path: '/contact-us', + getParentRoute: () => LandingRoute, +} as any) + +const LandingAboutUsRoute = LandingAboutUsImport.update({ + id: '/about-us', + path: '/about-us', + getParentRoute: () => LandingRoute, +} as any) + // Populate the FileRoutesByPath interface declare module '@tanstack/react-router' { @@ -163,11 +169,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexLazyImport parentRoute: typeof rootRoute } - '/about-us': { - id: '/about-us' - path: '/about-us' - fullPath: '/about-us' - preLoaderRoute: typeof AboutUsImport + '/_landing': { + id: '/_landing' + path: '' + fullPath: '' + preLoaderRoute: typeof LandingImport parentRoute: typeof rootRoute } '/account': { @@ -184,47 +190,54 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthImport parentRoute: typeof rootRoute } - '/contact-us': { - id: '/contact-us' + '/_landing/about-us': { + id: '/_landing/about-us' + path: '/about-us' + fullPath: '/about-us' + preLoaderRoute: typeof LandingAboutUsImport + parentRoute: typeof LandingImport + } + '/_landing/contact-us': { + id: '/_landing/contact-us' path: '/contact-us' fullPath: '/contact-us' - preLoaderRoute: typeof ContactUsImport - parentRoute: typeof rootRoute + preLoaderRoute: typeof LandingContactUsImport + parentRoute: typeof LandingImport } - '/disclaimer': { - id: '/disclaimer' + '/_landing/disclaimer': { + id: '/_landing/disclaimer' path: '/disclaimer' fullPath: '/disclaimer' - preLoaderRoute: typeof DisclaimerImport - parentRoute: typeof rootRoute + preLoaderRoute: typeof LandingDisclaimerImport + parentRoute: typeof LandingImport } - '/features': { - id: '/features' + '/_landing/features': { + id: '/_landing/features' path: '/features' fullPath: '/features' - preLoaderRoute: typeof FeaturesImport - parentRoute: typeof rootRoute + preLoaderRoute: typeof LandingFeaturesImport + parentRoute: typeof LandingImport } - '/policy': { - id: '/policy' + '/_landing/policy': { + id: '/_landing/policy' path: '/policy' fullPath: '/policy' - preLoaderRoute: typeof PolicyImport - parentRoute: typeof rootRoute + preLoaderRoute: typeof LandingPolicyImport + parentRoute: typeof LandingImport } - '/support': { - id: '/support' + '/_landing/support': { + id: '/_landing/support' path: '/support' fullPath: '/support' - preLoaderRoute: typeof SupportImport - parentRoute: typeof rootRoute + preLoaderRoute: typeof LandingSupportImport + parentRoute: typeof LandingImport } - '/terms': { - id: '/terms' + '/_landing/terms': { + id: '/_landing/terms' path: '/terms' fullPath: '/terms' - preLoaderRoute: typeof TermsImport - parentRoute: typeof rootRoute + preLoaderRoute: typeof LandingTermsImport + parentRoute: typeof LandingImport } '/account/settings': { id: '/account/settings' @@ -294,6 +307,29 @@ declare module '@tanstack/react-router' { // Create and export the route tree +interface LandingRouteChildren { + LandingAboutUsRoute: typeof LandingAboutUsRoute + LandingContactUsRoute: typeof LandingContactUsRoute + LandingDisclaimerRoute: typeof LandingDisclaimerRoute + LandingFeaturesRoute: typeof LandingFeaturesRoute + LandingPolicyRoute: typeof LandingPolicyRoute + LandingSupportRoute: typeof LandingSupportRoute + LandingTermsRoute: typeof LandingTermsRoute +} + +const LandingRouteChildren: LandingRouteChildren = { + LandingAboutUsRoute: LandingAboutUsRoute, + LandingContactUsRoute: LandingContactUsRoute, + LandingDisclaimerRoute: LandingDisclaimerRoute, + LandingFeaturesRoute: LandingFeaturesRoute, + LandingPolicyRoute: LandingPolicyRoute, + LandingSupportRoute: LandingSupportRoute, + LandingTermsRoute: LandingTermsRoute, +} + +const LandingRouteWithChildren = + LandingRoute._addFileChildren(LandingRouteChildren) + interface AccountRouteChildren { AccountSettingsRoute: typeof AccountSettingsRoute AccountStorageRoute: typeof AccountStorageRoute @@ -329,15 +365,16 @@ const AuthRouteWithChildren = AuthRoute._addFileChildren(AuthRouteChildren) export interface FileRoutesByFullPath { '/': typeof IndexLazyRoute - '/about-us': typeof AboutUsRoute + '': typeof LandingRouteWithChildren '/account': typeof AccountRouteWithChildren '/auth': typeof AuthRouteWithChildren - '/contact-us': typeof ContactUsRoute - '/disclaimer': typeof DisclaimerRoute - '/features': typeof FeaturesRoute - '/policy': typeof PolicyRoute - '/support': typeof SupportRoute - '/terms': typeof TermsRoute + '/about-us': typeof LandingAboutUsRoute + '/contact-us': typeof LandingContactUsRoute + '/disclaimer': typeof LandingDisclaimerRoute + '/features': typeof LandingFeaturesRoute + '/policy': typeof LandingPolicyRoute + '/support': typeof LandingSupportRoute + '/terms': typeof LandingTermsRoute '/account/settings': typeof AccountSettingsRoute '/account/storage': typeof AccountStorageRoute '/auth/forgot-password': typeof AuthForgotPasswordRoute @@ -351,14 +388,15 @@ export interface FileRoutesByFullPath { export interface FileRoutesByTo { '/': typeof IndexLazyRoute - '/about-us': typeof AboutUsRoute + '': typeof LandingRouteWithChildren '/auth': typeof AuthRouteWithChildren - '/contact-us': typeof ContactUsRoute - '/disclaimer': typeof DisclaimerRoute - '/features': typeof FeaturesRoute - '/policy': typeof PolicyRoute - '/support': typeof SupportRoute - '/terms': typeof TermsRoute + '/about-us': typeof LandingAboutUsRoute + '/contact-us': typeof LandingContactUsRoute + '/disclaimer': typeof LandingDisclaimerRoute + '/features': typeof LandingFeaturesRoute + '/policy': typeof LandingPolicyRoute + '/support': typeof LandingSupportRoute + '/terms': typeof LandingTermsRoute '/account/settings': typeof AccountSettingsRoute '/account/storage': typeof AccountStorageRoute '/auth/forgot-password': typeof AuthForgotPasswordRoute @@ -373,15 +411,16 @@ export interface FileRoutesByTo { export interface FileRoutesById { __root__: typeof rootRoute '/': typeof IndexLazyRoute - '/about-us': typeof AboutUsRoute + '/_landing': typeof LandingRouteWithChildren '/account': typeof AccountRouteWithChildren '/auth': typeof AuthRouteWithChildren - '/contact-us': typeof ContactUsRoute - '/disclaimer': typeof DisclaimerRoute - '/features': typeof FeaturesRoute - '/policy': typeof PolicyRoute - '/support': typeof SupportRoute - '/terms': typeof TermsRoute + '/_landing/about-us': typeof LandingAboutUsRoute + '/_landing/contact-us': typeof LandingContactUsRoute + '/_landing/disclaimer': typeof LandingDisclaimerRoute + '/_landing/features': typeof LandingFeaturesRoute + '/_landing/policy': typeof LandingPolicyRoute + '/_landing/support': typeof LandingSupportRoute + '/_landing/terms': typeof LandingTermsRoute '/account/settings': typeof AccountSettingsRoute '/account/storage': typeof AccountStorageRoute '/auth/forgot-password': typeof AuthForgotPasswordRoute @@ -397,9 +436,10 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' - | '/about-us' + | '' | '/account' | '/auth' + | '/about-us' | '/contact-us' | '/disclaimer' | '/features' @@ -418,8 +458,9 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' - | '/about-us' + | '' | '/auth' + | '/about-us' | '/contact-us' | '/disclaimer' | '/features' @@ -438,15 +479,16 @@ export interface FileRouteTypes { id: | '__root__' | '/' - | '/about-us' + | '/_landing' | '/account' | '/auth' - | '/contact-us' - | '/disclaimer' - | '/features' - | '/policy' - | '/support' - | '/terms' + | '/_landing/about-us' + | '/_landing/contact-us' + | '/_landing/disclaimer' + | '/_landing/features' + | '/_landing/policy' + | '/_landing/support' + | '/_landing/terms' | '/account/settings' | '/account/storage' | '/auth/forgot-password' @@ -461,29 +503,17 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexLazyRoute: typeof IndexLazyRoute - AboutUsRoute: typeof AboutUsRoute + LandingRoute: typeof LandingRouteWithChildren AccountRoute: typeof AccountRouteWithChildren AuthRoute: typeof AuthRouteWithChildren - ContactUsRoute: typeof ContactUsRoute - DisclaimerRoute: typeof DisclaimerRoute - FeaturesRoute: typeof FeaturesRoute - PolicyRoute: typeof PolicyRoute - SupportRoute: typeof SupportRoute - TermsRoute: typeof TermsRoute EmailChangeRoute: typeof EmailChangeRoute } const rootRouteChildren: RootRouteChildren = { IndexLazyRoute: IndexLazyRoute, - AboutUsRoute: AboutUsRoute, + LandingRoute: LandingRouteWithChildren, AccountRoute: AccountRouteWithChildren, AuthRoute: AuthRouteWithChildren, - ContactUsRoute: ContactUsRoute, - DisclaimerRoute: DisclaimerRoute, - FeaturesRoute: FeaturesRoute, - PolicyRoute: PolicyRoute, - SupportRoute: SupportRoute, - TermsRoute: TermsRoute, EmailChangeRoute: EmailChangeRoute, } @@ -498,23 +528,26 @@ export const routeTree = rootRoute "filePath": "__root.tsx", "children": [ "/", - "/about-us", + "/_landing", "/account", "/auth", - "/contact-us", - "/disclaimer", - "/features", - "/policy", - "/support", - "/terms", "/email/change" ] }, "/": { "filePath": "index.lazy.tsx" }, - "/about-us": { - "filePath": "about-us.tsx" + "/_landing": { + "filePath": "_landing.tsx", + "children": [ + "/_landing/about-us", + "/_landing/contact-us", + "/_landing/disclaimer", + "/_landing/features", + "/_landing/policy", + "/_landing/support", + "/_landing/terms" + ] }, "/account": { "filePath": "account.tsx", @@ -534,23 +567,33 @@ export const routeTree = rootRoute "/auth/success" ] }, - "/contact-us": { - "filePath": "contact-us.tsx" + "/_landing/about-us": { + "filePath": "_landing/about-us.tsx", + "parent": "/_landing" + }, + "/_landing/contact-us": { + "filePath": "_landing/contact-us.tsx", + "parent": "/_landing" }, - "/disclaimer": { - "filePath": "disclaimer.tsx" + "/_landing/disclaimer": { + "filePath": "_landing/disclaimer.tsx", + "parent": "/_landing" }, - "/features": { - "filePath": "features.tsx" + "/_landing/features": { + "filePath": "_landing/features.tsx", + "parent": "/_landing" }, - "/policy": { - "filePath": "policy.tsx" + "/_landing/policy": { + "filePath": "_landing/policy.tsx", + "parent": "/_landing" }, - "/support": { - "filePath": "support.tsx" + "/_landing/support": { + "filePath": "_landing/support.tsx", + "parent": "/_landing" }, - "/terms": { - "filePath": "terms.tsx" + "/_landing/terms": { + "filePath": "_landing/terms.tsx", + "parent": "/_landing" }, "/account/settings": { "filePath": "account/settings.tsx", diff --git a/src/routes/_landing.tsx b/src/routes/_landing.tsx new file mode 100644 index 000000000..cb14b1c64 --- /dev/null +++ b/src/routes/_landing.tsx @@ -0,0 +1,95 @@ +import { Stack, Typography } from '@mui/material'; + +import { + DEFAULT_BACKGROUND_COLOR, + GraaspLogo, + useButtonColor, + useMobileView, +} from '@graasp/ui'; + +import { Link, Outlet, createFileRoute } from '@tanstack/react-router'; + +import { useAuth } from '@/AuthContext'; +import { ACCOUNT_HOME_PATH, LANDING_PAGE_PATH } from '@/config/paths'; + +import { Footer } from '~landing/footer/Footer'; +import { RightHeader } from '~landing/header/RightHeader'; + +export const Route = createFileRoute('/_landing')({ + component: RouteComponent, +}); + +function RouteComponent() { + const { isAuthenticated } = useAuth(); + const { isMobile } = useMobileView(); + const { fill: primary } = useButtonColor('primary'); + + return ( + + theme.shadows[3], + zIndex: (theme) => theme.zIndex.appBar, + }} + > + + + + {!isMobile && ( + + Graasp + + )} + + + + + + + +