Added smart language switch for posts ad hoc page and content
This commit is contained in:
		
							parent
							
								
									049a2e2044
								
							
						
					
					
						commit
						a84560d86e
					
				| @ -171,7 +171,7 @@ module.exports = { | ||||
|     "@typescript-eslint/no-require-imports": "error", | ||||
|     // "@typescript-eslint/no-type-alias": "warn",
 | ||||
|     "@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn", | ||||
|     // "@typescript-eslint/no-unnecessary-condition": "warn",
 | ||||
|     "@typescript-eslint/no-unnecessary-condition": "warn", | ||||
|     "@typescript-eslint/no-unnecessary-qualifier": "warn", | ||||
|     "@typescript-eslint/no-unnecessary-type-arguments": "warn", | ||||
|     "@typescript-eslint/prefer-enum-initializers": "error", | ||||
|  | ||||
| @ -11,42 +11,61 @@ interface Props { | ||||
|   target?: "_blank"; | ||||
|   onClick?: MouseEventHandler<HTMLDivElement>; | ||||
|   draggable?: boolean; | ||||
|   badgeNumber?: number; | ||||
| } | ||||
| 
 | ||||
| export default function Button(props: Props): JSX.Element { | ||||
|   const { | ||||
|     draggable, | ||||
|     id, | ||||
|     onClick, | ||||
|     active, | ||||
|     className, | ||||
|     children, | ||||
|     target, | ||||
|     href, | ||||
|     locale, | ||||
|     badgeNumber, | ||||
|   } = props; | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const button = ( | ||||
|     <div | ||||
|       draggable={props.draggable} | ||||
|       id={props.id} | ||||
|       onClick={props.onClick} | ||||
|       className={`grid place-content-center place-items-center border-[1px] border-dark text-dark rounded-full px-4 pt-[0.4rem] pb-[0.5rem] transition-all select-none ${ | ||||
|         props.className | ||||
|       } ${ | ||||
|         props.active | ||||
|       draggable={draggable} | ||||
|       id={id} | ||||
|       onClick={onClick} | ||||
|       className={`grid place-content-center place-items-center border-[1px]
 | ||||
|       border-dark text-dark rounded-full px-4 pt-[0.4rem] pb-[0.5rem] | ||||
|       transition-all select-none hover:[--opacityBadge:0] --opacityBadge:100 ${className} ${ | ||||
|         active | ||||
|           ? "text-light bg-black drop-shadow-black-lg !border-black cursor-not-allowed" | ||||
|           : "cursor-pointer hover:text-light hover:bg-dark hover:drop-shadow-shade-lg active:bg-black active:text-light active:drop-shadow-black-lg active:border-black" | ||||
|       }`}
 | ||||
|     > | ||||
|       {props.children} | ||||
|       {badgeNumber && ( | ||||
|         <div className="opacity-[var(--opacityBadge)] transition-opacity grid place-items-center absolute -top-3 -right-2 bg-dark w-8 h-8 text-light font-bold rounded-full"> | ||||
|           {badgeNumber} | ||||
|         </div> | ||||
|       )} | ||||
|       {children} | ||||
|     </div> | ||||
|   ); | ||||
| 
 | ||||
|   if (props.target) { | ||||
|   if (target) { | ||||
|     return ( | ||||
|       <a href={props.href} target={props.target} rel="noreferrer"> | ||||
|         <div>{button}</div> | ||||
|       <a href={href} target={target} rel="noreferrer"> | ||||
|         <div className="relative">{button}</div> | ||||
|       </a> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className="relative" | ||||
|       onClick={() => { | ||||
|         if (props.href || props.locale) | ||||
|           router.push(props.href ?? router.asPath, props.href, { | ||||
|             locale: props.locale, | ||||
|         if (href || locale) | ||||
|           router.push(href ?? router.asPath, href, { | ||||
|             locale: locale, | ||||
|           }); | ||||
|       }} | ||||
|     > | ||||
|  | ||||
| @ -26,6 +26,7 @@ interface Props { | ||||
|   >["categories"]; | ||||
|   thumbnail?: UploadImageFragment | null | undefined; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   languageSwitcher?: JSX.Element; | ||||
| } | ||||
| 
 | ||||
| export default function ThumbnailHeader(props: Props): JSX.Element { | ||||
| @ -38,6 +39,7 @@ export default function ThumbnailHeader(props: Props): JSX.Element { | ||||
|     type, | ||||
|     categories, | ||||
|     description, | ||||
|     languageSwitcher, | ||||
|   } = props; | ||||
| 
 | ||||
|   return ( | ||||
| @ -67,7 +69,7 @@ export default function ThumbnailHeader(props: Props): JSX.Element { | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className="grid grid-flow-col gap-8"> | ||||
|       <div className="flex place-content-center flex-row flew-wrap gap-8"> | ||||
|         {type?.data?.attributes && ( | ||||
|           <div className="flex flex-col place-items-center gap-2"> | ||||
|             <h3 className="text-xl">{langui.type}</h3> | ||||
| @ -92,6 +94,7 @@ export default function ThumbnailHeader(props: Props): JSX.Element { | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|         {languageSwitcher} | ||||
|       </div> | ||||
|       {description && <InsetBox className="mt-8">{description}</InsetBox>} | ||||
|     </> | ||||
|  | ||||
| @ -1,33 +1,31 @@ | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyLanguage } from "queries/helpers"; | ||||
| import { Dispatch, SetStateAction } from "react"; | ||||
| import Button from "./Button"; | ||||
| import ToolTip from "./ToolTip"; | ||||
| 
 | ||||
| interface Props { | ||||
|   className?: string; | ||||
|   locales: (string | undefined)[]; | ||||
|   languages: AppStaticProps["languages"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   href?: string; | ||||
|   locales: Map<string, number>; | ||||
|   localesIndex: number | undefined; | ||||
|   setLocalesIndex: Dispatch<SetStateAction<number | undefined>>; | ||||
| } | ||||
| 
 | ||||
| export default function LanguageSwitcher(props: Props): JSX.Element { | ||||
|   const { locales, langui, href } = props; | ||||
|   const router = useRouter(); | ||||
|   const { locales, localesIndex, setLocalesIndex } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="w-full grid place-content-center"> | ||||
|       <div className="flex flex-col place-items-center text-center gap-4 my-12 border-2 border-mid rounded-xl p-8 max-w-lg"> | ||||
|         <p>{langui.language_switch_message}</p> | ||||
|         <div className="flex flex-wrap flex-row gap-2"> | ||||
|           {locales.map((locale, index) => ( | ||||
|     <ToolTip | ||||
|       content={ | ||||
|         <div className="flex flex-col gap-2"> | ||||
|           {[...locales].map(([locale, value], index) => ( | ||||
|             <> | ||||
|               {locale && ( | ||||
|                 <Button | ||||
|                   key={index} | ||||
|                   active={locale === router.locale} | ||||
|                   href={href} | ||||
|                   locale={locale} | ||||
|                   active={value === localesIndex} | ||||
|                   onClick={() => setLocalesIndex(value)} | ||||
|                 > | ||||
|                   {prettyLanguage(locale, props.languages)} | ||||
|                 </Button> | ||||
| @ -35,7 +33,11 @@ export default function LanguageSwitcher(props: Props): JSX.Element { | ||||
|             </> | ||||
|           ))} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|       } | ||||
|     > | ||||
|       <Button badgeNumber={locales.size}> | ||||
|         <span className="material-icons">translate</span> | ||||
|       </Button> | ||||
|     </ToolTip> | ||||
|   ); | ||||
| } | ||||
|  | ||||
							
								
								
									
										230
									
								
								src/components/Post.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								src/components/Post.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,230 @@ | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { | ||||
|   getPreferredLanguage, | ||||
|   getStatusDescription, | ||||
|   prettySlug, | ||||
| } from "queries/helpers"; | ||||
| import { useEffect, useMemo, useState } from "react"; | ||||
| import AppLayout from "./AppLayout"; | ||||
| import Chip from "./Chip"; | ||||
| import ThumbnailHeader from "./Content/ThumbnailHeader"; | ||||
| import HorizontalLine from "./HorizontalLine"; | ||||
| import LanguageSwitcher from "./LanguageSwitcher"; | ||||
| import Markdawn from "./Markdown/Markdawn"; | ||||
| import TOC from "./Markdown/TOC"; | ||||
| import ReturnButton, { ReturnButtonType } from "./PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "./Panels/ContentPanel"; | ||||
| import SubPanel from "./Panels/SubPanel"; | ||||
| import RecorderChip from "./RecorderChip"; | ||||
| import ToolTip from "./ToolTip"; | ||||
| 
 | ||||
| interface Props { | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   languages: AppStaticProps["languages"]; | ||||
|   currencies: AppStaticProps["currencies"]; | ||||
|   returnHref?: string; | ||||
|   returnTitle?: string | null | undefined; | ||||
|   displayCredits?: boolean; | ||||
|   displayToc?: boolean; | ||||
|   displayThumbnailHeader?: boolean; | ||||
|   displayTitle?: boolean; | ||||
|   displayLanguageSwitcher?: boolean; | ||||
|   prependBody?: JSX.Element; | ||||
|   appendBody?: JSX.Element; | ||||
| } | ||||
| 
 | ||||
| export default function Post(props: Props): JSX.Element { | ||||
|   const { | ||||
|     post, | ||||
|     langui, | ||||
|     languages, | ||||
|     returnHref, | ||||
|     returnTitle, | ||||
|     displayCredits, | ||||
|     displayToc, | ||||
|     displayThumbnailHeader, | ||||
|     displayLanguageSwitcher, | ||||
|     appendBody, | ||||
|     prependBody, | ||||
|   } = props; | ||||
|   const displayTitle = props.displayTitle ?? true; | ||||
| 
 | ||||
|   const appLayout = useAppLayout(); | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const [selectedTranslation, setSelectedTranslation] = useState< | ||||
|     | Exclude< | ||||
|         Exclude<Props["post"], null | undefined>["translations"], | ||||
|         null | undefined | ||||
|       >[number] | ||||
|   >(); | ||||
|   const translationLocales: Map<string, number> = new Map(); | ||||
| 
 | ||||
|   const [selectedTranslationIndex, setSelectedTranslationIndex] = useState< | ||||
|     number | undefined | ||||
|   >(); | ||||
| 
 | ||||
|   if (post?.translations) { | ||||
|     post.translations.map((translation, index) => { | ||||
|       if (translation?.language?.data?.attributes?.code) { | ||||
|         translationLocales.set( | ||||
|           translation.language.data.attributes.code, | ||||
|           index | ||||
|         ); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   useMemo(() => { | ||||
|     setSelectedTranslationIndex( | ||||
|       getPreferredLanguage( | ||||
|         appLayout.preferredLanguages ?? [router.locale], | ||||
|         translationLocales | ||||
|       ) | ||||
|     ); | ||||
|   }, [appLayout.preferredLanguages]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (selectedTranslationIndex !== undefined) | ||||
|       setSelectedTranslation(post?.translations?.[selectedTranslationIndex]); | ||||
|   }, [selectedTranslationIndex]); | ||||
| 
 | ||||
|   const thumbnail = | ||||
|     selectedTranslation?.thumbnail?.data?.attributes ?? | ||||
|     post?.thumbnail?.data?.attributes; | ||||
| 
 | ||||
|   const body = selectedTranslation?.body ?? ""; | ||||
|   const title = selectedTranslation?.title ?? prettySlug(post?.slug); | ||||
|   const except = selectedTranslation?.excerpt ?? ""; | ||||
| 
 | ||||
|   const subPanel = | ||||
|     returnHref || returnTitle || displayCredits || displayToc ? ( | ||||
|       <SubPanel> | ||||
|         {returnHref && returnTitle && ( | ||||
|           <ReturnButton | ||||
|             href={returnHref} | ||||
|             title={returnTitle} | ||||
|             langui={langui} | ||||
|             displayOn={ReturnButtonType.desktop} | ||||
|             horizontalLine | ||||
|           /> | ||||
|         )} | ||||
| 
 | ||||
|         {displayCredits && ( | ||||
|           <> | ||||
|             {selectedTranslation && ( | ||||
|               <div className="grid grid-flow-col place-items-center place-content-center gap-2"> | ||||
|                 <p className="font-headers">{langui.status}:</p> | ||||
| 
 | ||||
|                 <ToolTip | ||||
|                   content={getStatusDescription( | ||||
|                     selectedTranslation.status, | ||||
|                     langui | ||||
|                   )} | ||||
|                   maxWidth={"20rem"} | ||||
|                 > | ||||
|                   <Chip>{selectedTranslation.status}</Chip> | ||||
|                 </ToolTip> | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|             {post?.authors && post.authors.data.length > 0 && ( | ||||
|               <div> | ||||
|                 <p className="font-headers">{"Authors"}:</p> | ||||
|                 <div className="grid place-items-center place-content-center gap-2"> | ||||
|                   {post.authors.data.map((author) => ( | ||||
|                     <> | ||||
|                       {author.attributes && ( | ||||
|                         <RecorderChip | ||||
|                           key={author.id} | ||||
|                           langui={langui} | ||||
|                           recorder={author.attributes} | ||||
|                         /> | ||||
|                       )} | ||||
|                     </> | ||||
|                   ))} | ||||
|                 </div> | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|             <HorizontalLine /> | ||||
|           </> | ||||
|         )} | ||||
| 
 | ||||
|         {displayToc && <TOC text={body} title={title} />} | ||||
|       </SubPanel> | ||||
|     ) : undefined; | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/news" | ||||
|         title={langui.news} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
| 
 | ||||
|       {displayThumbnailHeader ? ( | ||||
|         <> | ||||
|           <ThumbnailHeader | ||||
|             thumbnail={thumbnail} | ||||
|             title={title} | ||||
|             description={except} | ||||
|             langui={langui} | ||||
|             categories={post?.categories} | ||||
|             languageSwitcher={ | ||||
|               <LanguageSwitcher | ||||
|                 languages={languages} | ||||
|                 locales={translationLocales} | ||||
|                 localesIndex={selectedTranslationIndex} | ||||
|                 setLocalesIndex={setSelectedTranslationIndex} | ||||
|               /> | ||||
|             } | ||||
|           /> | ||||
| 
 | ||||
|           <HorizontalLine /> | ||||
|         </> | ||||
|       ) : ( | ||||
|         <> | ||||
|           {displayLanguageSwitcher && ( | ||||
|             <div className="grid place-content-end place-items-start"> | ||||
|               <LanguageSwitcher | ||||
|                 languages={languages} | ||||
|                 locales={translationLocales} | ||||
|                 localesIndex={selectedTranslationIndex} | ||||
|                 setLocalesIndex={setSelectedTranslationIndex} | ||||
|               /> | ||||
|             </div> | ||||
|           )} | ||||
|           {displayTitle && ( | ||||
|             <h1 className="text-center flex gap-3 justify-center text-4xl my-16"> | ||||
|               {title} | ||||
|             </h1> | ||||
|           )} | ||||
|         </> | ||||
|       )} | ||||
| 
 | ||||
|       {prependBody} | ||||
|       <Markdawn text={body} /> | ||||
|       {appendBody} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={title} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       thumbnail={thumbnail ?? undefined} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
| @ -4,7 +4,7 @@ query getContentText($slug: String, $language_code: String) { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|         titles { | ||||
|           pre_title | ||||
|           title | ||||
|           subtitle | ||||
| @ -56,7 +56,9 @@ query getContentText($slug: String, $language_code: String) { | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         text_set_languages: text_set { | ||||
|         text_set { | ||||
|           status | ||||
|           text | ||||
|           language { | ||||
|             data { | ||||
|               attributes { | ||||
| @ -64,10 +66,6 @@ query getContentText($slug: String, $language_code: String) { | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         text_set(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           status | ||||
|           text | ||||
|           source_language { | ||||
|             data { | ||||
|               attributes { | ||||
|  | ||||
| @ -29,7 +29,8 @@ query getLibraryItemScans($slug: String, $language_code: String) { | ||||
|                   ending_time | ||||
|                 } | ||||
|               } | ||||
|               scan_set_languages: scan_set { | ||||
|               scan_set { | ||||
|                 status | ||||
|                 language { | ||||
|                   data { | ||||
|                     attributes { | ||||
| @ -37,16 +38,6 @@ query getLibraryItemScans($slug: String, $language_code: String) { | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|               scan_set( | ||||
|                 filters: { | ||||
|                   or: [ | ||||
|                     { language: { code: { eq: "xx" } } } | ||||
|                     { language: { code: { eq: $language_code } } } | ||||
|                   ] | ||||
|                 } | ||||
|               ) { | ||||
|                 status | ||||
|                 source_language { | ||||
|                   data { | ||||
|                     attributes { | ||||
|  | ||||
| @ -33,7 +33,7 @@ query getPost($slug: String, $language_code: String) { | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         translations_languages: translations { | ||||
|         translations { | ||||
|           language { | ||||
|             data { | ||||
|               attributes { | ||||
| @ -41,8 +41,6 @@ query getPost($slug: String, $language_code: String) { | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         translations(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           status | ||||
|           title | ||||
|           excerpt | ||||
|  | ||||
| @ -1,18 +1,8 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import TOC from "components/Markdown/TOC"; | ||||
| import ReturnButton, { | ||||
|   ReturnButtonType, | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import Post from "components/Post"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { getLocalesFromLanguages, prettySlug } from "queries/helpers"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
| @ -22,53 +12,17 @@ interface Props extends AppStaticProps { | ||||
| } | ||||
| 
 | ||||
| export default function AccordsHandbook(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const router = useRouter(); | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
|           languages={props.languages} | ||||
|           langui={props.langui} | ||||
|         /> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const { post, langui, languages, currencies } = props; | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={title} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
|     <Post | ||||
|       currencies={currencies} | ||||
|       languages={languages} | ||||
|       langui={langui} | ||||
|       post={post} | ||||
|       returnHref="/about-us/" | ||||
|       returnTitle={langui.about_us} | ||||
|       displayToc | ||||
|       displayLanguageSwitcher | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,12 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import InsetBox from "components/InsetBox"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import TOC from "components/Markdown/TOC"; | ||||
| import ReturnButton, { | ||||
|   ReturnButtonType, | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import Post from "components/Post"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { RequestMailProps, ResponseMailProps } from "pages/api/mail"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { | ||||
|   getLocalesFromLanguages, | ||||
|   prettySlug, | ||||
|   randomInt, | ||||
| } from "queries/helpers"; | ||||
| import { randomInt } from "queries/helpers"; | ||||
| import { useState } from "react"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
| @ -29,200 +17,170 @@ interface Props extends AppStaticProps { | ||||
| } | ||||
| 
 | ||||
| export default function AboutUs(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const { post, langui, languages, currencies } = props; | ||||
| 
 | ||||
|   const router = useRouter(); | ||||
|   const [formResponse, setFormResponse] = useState(""); | ||||
|   const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">( | ||||
|     "stale" | ||||
|   ); | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
| 
 | ||||
|   const [randomNumber1, setRandomNumber1] = useState(randomInt(0, 10)); | ||||
|   const [randomNumber2, setRandomNumber2] = useState(randomInt(0, 10)); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
|   const contactForm = ( | ||||
|     <div className="flex flex-col gap-8 text-center"> | ||||
|       <form | ||||
|         className={`gap-8 grid ${ | ||||
|           formState !== "stale" && | ||||
|           "opacity-60 cursor-not-allowed touch-none pointer-events-none" | ||||
|         }`}
 | ||||
|         onSubmit={(event) => { | ||||
|           event.preventDefault(); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
|           const fields = event.target as unknown as { | ||||
|             verif: HTMLInputElement; | ||||
|             name: HTMLInputElement; | ||||
|             email: HTMLInputElement; | ||||
|             message: HTMLInputElement; | ||||
|           }; | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
|           languages={props.languages} | ||||
|           langui={props.langui} | ||||
|         /> | ||||
|       )} | ||||
|           setFormState("ongoing"); | ||||
| 
 | ||||
|       <div className="flex flex-col gap-8 text-center"> | ||||
|         <form | ||||
|           className={`gap-8 grid ${ | ||||
|             formState !== "stale" && | ||||
|             "opacity-60 cursor-not-allowed touch-none pointer-events-none" | ||||
|           }`}
 | ||||
|           onSubmit={(event) => { | ||||
|             event.preventDefault(); | ||||
| 
 | ||||
|             const fields = event.target as unknown as { | ||||
|               verif: HTMLInputElement; | ||||
|               name: HTMLInputElement; | ||||
|               email: HTMLInputElement; | ||||
|               message: HTMLInputElement; | ||||
|           if ( | ||||
|             parseInt(fields.verif.value, 10) === | ||||
|               randomNumber1 + randomNumber2 && | ||||
|             formState !== "completed" | ||||
|           ) { | ||||
|             const content: RequestMailProps = { | ||||
|               name: fields.name.value, | ||||
|               email: fields.email.value, | ||||
|               message: fields.message.value, | ||||
|               formName: "Contact Form", | ||||
|             }; | ||||
|             fetch("/api/mail", { | ||||
|               method: "POST", | ||||
|               body: JSON.stringify(content), | ||||
|               headers: { | ||||
|                 "Content-type": "application/json; charset=UTF-8", | ||||
|               }, | ||||
|             }) | ||||
|               .then(async (responseJson) => responseJson.json()) | ||||
|               .then((response: ResponseMailProps) => { | ||||
|                 switch (response.code) { | ||||
|                   case "OKAY": | ||||
|                     setFormResponse(langui.response_email_success ?? ""); | ||||
|                     setFormState("completed"); | ||||
| 
 | ||||
|             setFormState("ongoing"); | ||||
|                     break; | ||||
| 
 | ||||
|             if ( | ||||
|               parseInt(fields.verif.value, 10) === | ||||
|                 randomNumber1 + randomNumber2 && | ||||
|               formState !== "completed" | ||||
|             ) { | ||||
|               const content: RequestMailProps = { | ||||
|                 name: fields.name.value, | ||||
|                 email: fields.email.value, | ||||
|                 message: fields.message.value, | ||||
|                 formName: "Contact Form", | ||||
|               }; | ||||
|               fetch("/api/mail", { | ||||
|                 method: "POST", | ||||
|                 body: JSON.stringify(content), | ||||
|                 headers: { | ||||
|                   "Content-type": "application/json; charset=UTF-8", | ||||
|                 }, | ||||
|               }) | ||||
|                 .then(async (responseJson) => responseJson.json()) | ||||
|                 .then((response: ResponseMailProps) => { | ||||
|                   switch (response.code) { | ||||
|                     case "OKAY": | ||||
|                       setFormResponse(langui.response_email_success ?? ""); | ||||
|                       setFormState("completed"); | ||||
|                   case "EENVELOPE": | ||||
|                     setFormResponse(langui.response_invalid_email ?? ""); | ||||
|                     setFormState("stale"); | ||||
|                     break; | ||||
| 
 | ||||
|                       break; | ||||
|                   default: | ||||
|                     setFormResponse(response.message ?? ""); | ||||
|                     setFormState("stale"); | ||||
|                     break; | ||||
|                 } | ||||
|               }); | ||||
|           } else { | ||||
|             setFormResponse(langui.response_invalid_code ?? ""); | ||||
|             setFormState("stale"); | ||||
|             setRandomNumber1(randomInt(0, 10)); | ||||
|             setRandomNumber2(randomInt(0, 10)); | ||||
|           } | ||||
| 
 | ||||
|                     case "EENVELOPE": | ||||
|                       setFormResponse(langui.response_invalid_email ?? ""); | ||||
|                       setFormState("stale"); | ||||
|                       break; | ||||
| 
 | ||||
|                     default: | ||||
|                       setFormResponse(response.message ?? ""); | ||||
|                       setFormState("stale"); | ||||
|                       break; | ||||
|                   } | ||||
|                 }); | ||||
|             } else { | ||||
|               setFormResponse(langui.response_invalid_code ?? ""); | ||||
|               setFormState("stale"); | ||||
|               setRandomNumber1(randomInt(0, 10)); | ||||
|               setRandomNumber2(randomInt(0, 10)); | ||||
|             } | ||||
| 
 | ||||
|             router.replace("#send-response"); | ||||
|             fields.verif.value = ""; | ||||
|           }} | ||||
|         > | ||||
|           <div className="flex flex-col place-items-center gap-1"> | ||||
|             <label htmlFor="name">{langui.name}:</label> | ||||
|             <input | ||||
|               type="text" | ||||
|               className="mobile:w-full" | ||||
|               name="name" | ||||
|               id="name" | ||||
|               required | ||||
|               disabled={formState !== "stale"} | ||||
|             /> | ||||
|           </div> | ||||
| 
 | ||||
|           <div className="flex flex-col place-items-center gap-1"> | ||||
|             <label htmlFor="email">{langui.email}:</label> | ||||
|             <input | ||||
|               type="email" | ||||
|               className="mobile:w-full" | ||||
|               name="email" | ||||
|               id="email" | ||||
|               required | ||||
|               disabled={formState !== "stale"} | ||||
|             /> | ||||
|             <p className="text-sm text-dark italic opacity-70"> | ||||
|               {langui.email_gdpr_notice} | ||||
|             </p> | ||||
|           </div> | ||||
| 
 | ||||
|           <div className="flex flex-col place-items-center gap-1 w-full"> | ||||
|             <label htmlFor="message">{langui.message}:</label> | ||||
|             <textarea | ||||
|               name="message" | ||||
|               id="message" | ||||
|               className="w-full" | ||||
|               rows={8} | ||||
|               required | ||||
|               disabled={formState !== "stale"} | ||||
|             /> | ||||
|           </div> | ||||
| 
 | ||||
|           <div className="grid grid-cols-2 place-items-center"> | ||||
|             <div className="flex flex-row place-items-center gap-2"> | ||||
|               <label | ||||
|                 className="flex-shrink-0" | ||||
|                 htmlFor="verif" | ||||
|               >{`${randomNumber1} + ${randomNumber2} =`}</label> | ||||
|               <input | ||||
|                 className="w-24" | ||||
|                 type="number" | ||||
|                 name="verif" | ||||
|                 id="verif" | ||||
|                 required | ||||
|                 disabled={formState !== "stale"} | ||||
|               /> | ||||
|             </div> | ||||
| 
 | ||||
|             <input | ||||
|               type="submit" | ||||
|               value={langui.send ?? "Send"} | ||||
|               className="w-min !px-6" | ||||
|               disabled={formState !== "stale"} | ||||
|             /> | ||||
|           </div> | ||||
|         </form> | ||||
| 
 | ||||
|         <div id="send-response"> | ||||
|           {formResponse && ( | ||||
|             <InsetBox> | ||||
|               <p>{formResponse}</p> | ||||
|             </InsetBox> | ||||
|           )} | ||||
|           router.replace("#send-response"); | ||||
|           fields.verif.value = ""; | ||||
|         }} | ||||
|       > | ||||
|         <div className="flex flex-col place-items-center gap-1"> | ||||
|           <label htmlFor="name">{langui.name}:</label> | ||||
|           <input | ||||
|             type="text" | ||||
|             className="mobile:w-full" | ||||
|             name="name" | ||||
|             id="name" | ||||
|             required | ||||
|             disabled={formState !== "stale"} | ||||
|           /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div className="flex flex-col place-items-center gap-1"> | ||||
|           <label htmlFor="email">{langui.email}:</label> | ||||
|           <input | ||||
|             type="email" | ||||
|             className="mobile:w-full" | ||||
|             name="email" | ||||
|             id="email" | ||||
|             required | ||||
|             disabled={formState !== "stale"} | ||||
|           /> | ||||
|           <p className="text-sm text-dark italic opacity-70"> | ||||
|             {langui.email_gdpr_notice} | ||||
|           </p> | ||||
|         </div> | ||||
| 
 | ||||
|         <div className="flex flex-col place-items-center gap-1 w-full"> | ||||
|           <label htmlFor="message">{langui.message}:</label> | ||||
|           <textarea | ||||
|             name="message" | ||||
|             id="message" | ||||
|             className="w-full" | ||||
|             rows={8} | ||||
|             required | ||||
|             disabled={formState !== "stale"} | ||||
|           /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div className="grid grid-cols-2 place-items-center"> | ||||
|           <div className="flex flex-row place-items-center gap-2"> | ||||
|             <label | ||||
|               className="flex-shrink-0" | ||||
|               htmlFor="verif" | ||||
|             >{`${randomNumber1} + ${randomNumber2} =`}</label> | ||||
|             <input | ||||
|               className="w-24" | ||||
|               type="number" | ||||
|               name="verif" | ||||
|               id="verif" | ||||
|               required | ||||
|               disabled={formState !== "stale"} | ||||
|             /> | ||||
|           </div> | ||||
| 
 | ||||
|           <input | ||||
|             type="submit" | ||||
|             value={langui.send ?? "Send"} | ||||
|             className="w-min !px-6" | ||||
|             disabled={formState !== "stale"} | ||||
|           /> | ||||
|         </div> | ||||
|       </form> | ||||
| 
 | ||||
|       <div id="send-response"> | ||||
|         {formResponse && ( | ||||
|           <InsetBox> | ||||
|             <p>{formResponse}</p> | ||||
|           </InsetBox> | ||||
|         )} | ||||
|       </div> | ||||
|     </ContentPanel> | ||||
|     </div> | ||||
|   ); | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={"Contact"} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
|     <Post | ||||
|       currencies={currencies} | ||||
|       languages={languages} | ||||
|       langui={langui} | ||||
|       post={post} | ||||
|       returnHref="/about-us/" | ||||
|       returnTitle={langui.about_us} | ||||
|       displayToc | ||||
|       appendBody={contactForm} | ||||
|       displayLanguageSwitcher | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,8 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import TOC from "components/Markdown/TOC"; | ||||
| import ReturnButton, { | ||||
|   ReturnButtonType, | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import Post from "components/Post"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { getLocalesFromLanguages, prettySlug } from "queries/helpers"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
| @ -22,53 +12,17 @@ interface Props extends AppStaticProps { | ||||
| } | ||||
| 
 | ||||
| export default function SiteInformation(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const router = useRouter(); | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
|           languages={props.languages} | ||||
|           langui={props.langui} | ||||
|         /> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const { post, langui, languages, currencies } = props; | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={title} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
|     <Post | ||||
|       currencies={currencies} | ||||
|       languages={languages} | ||||
|       langui={langui} | ||||
|       post={post} | ||||
|       returnHref="/about-us/" | ||||
|       returnTitle={langui.about_us} | ||||
|       displayToc | ||||
|       displayLanguageSwitcher | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,8 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import TOC from "components/Markdown/TOC"; | ||||
| import ReturnButton, { | ||||
|   ReturnButtonType, | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import Post from "components/Post"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { getLocalesFromLanguages, prettySlug } from "queries/helpers"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
| @ -21,53 +11,17 @@ interface Props extends AppStaticProps { | ||||
|   >["data"][number]["attributes"]; | ||||
| } | ||||
| export default function SharingPolicy(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/about-us" | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         langui={langui} | ||||
|         title={langui.about_us} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
|           languages={props.languages} | ||||
|           langui={props.langui} | ||||
|         /> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const { post, langui, languages, currencies } = props; | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={title} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
|     <Post | ||||
|       currencies={currencies} | ||||
|       languages={languages} | ||||
|       langui={langui} | ||||
|       post={post} | ||||
|       returnHref="/about-us/" | ||||
|       returnTitle={langui.about_us} | ||||
|       displayToc | ||||
|       displayLanguageSwitcher | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -13,6 +13,7 @@ import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import RecorderChip from "components/RecorderChip"; | ||||
| import ToolTip from "components/ToolTip"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { GetContentTextQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { | ||||
| @ -23,7 +24,7 @@ import { | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { | ||||
|   getLocalesFromLanguages, | ||||
|   getPreferredLanguage, | ||||
|   getStatusDescription, | ||||
|   prettyinlineTitle, | ||||
|   prettyLanguage, | ||||
| @ -31,6 +32,7 @@ import { | ||||
|   prettyTestError, | ||||
|   prettyTestWarning, | ||||
| } from "queries/helpers"; | ||||
| import { useEffect, useMemo, useState } from "react"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   content: Exclude< | ||||
| @ -47,7 +49,49 @@ export default function Content(props: Props): JSX.Element { | ||||
|   useTesting(props); | ||||
|   const { langui, content, languages } = props; | ||||
|   const router = useRouter(); | ||||
|   const locales = getLocalesFromLanguages(content?.text_set_languages); | ||||
|   const appLayout = useAppLayout(); | ||||
| 
 | ||||
|   const [selectedTextSet, setSelectedTextSet] = useState< | ||||
|     | Exclude< | ||||
|         Exclude<Props["content"], null | undefined>["text_set"], | ||||
|         null | undefined | ||||
|       >[number] | ||||
|   >(); | ||||
|   const [selectedTitle, setSelectedTitle] = useState< | ||||
|     | Exclude< | ||||
|         Exclude<Props["content"], null | undefined>["titles"], | ||||
|         null | undefined | ||||
|       >[number] | ||||
|   >(); | ||||
|   const textSetLocales: Map<string, number> = new Map(); | ||||
| 
 | ||||
|   const [selectedTextSetIndex, setSelectedTextSetIndex] = useState< | ||||
|     number | undefined | ||||
|   >(); | ||||
| 
 | ||||
|   if (content?.text_set) { | ||||
|     content.text_set.map((textSet, index) => { | ||||
|       if (textSet?.language?.data?.attributes?.code && textSet.text) { | ||||
|         textSetLocales.set(textSet.language.data.attributes.code, index); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   useMemo(() => { | ||||
|     setSelectedTextSetIndex( | ||||
|       getPreferredLanguage( | ||||
|         appLayout.preferredLanguages ?? [router.locale], | ||||
|         textSetLocales | ||||
|       ) | ||||
|     ); | ||||
|   }, [appLayout.preferredLanguages]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (selectedTextSetIndex !== undefined) | ||||
|       setSelectedTextSet(content?.text_set?.[selectedTextSetIndex]); | ||||
|     if (selectedTextSetIndex !== undefined) | ||||
|       setSelectedTitle(content?.titles?.[selectedTextSetIndex]); | ||||
|   }, [selectedTextSetIndex]); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
| @ -59,27 +103,25 @@ export default function Content(props: Props): JSX.Element { | ||||
|         horizontalLine | ||||
|       /> | ||||
| 
 | ||||
|       {content?.text_set?.[0]?.source_language?.data?.attributes && ( | ||||
|       {selectedTextSet?.source_language?.data?.attributes && ( | ||||
|         <div className="grid gap-5"> | ||||
|           <h2 className="text-xl"> | ||||
|             {content.text_set[0].source_language.data.attributes.code === | ||||
|             router.locale | ||||
|             {selectedTextSet.source_language.data.attributes.code === | ||||
|             selectedTextSet.language?.data?.attributes?.code | ||||
|               ? langui.transcript_notice | ||||
|               : langui.translation_notice} | ||||
|           </h2> | ||||
| 
 | ||||
|           {content.text_set[0].source_language.data.attributes.code !== | ||||
|             router.locale && ( | ||||
|           {selectedTextSet.source_language.data.attributes.code !== | ||||
|             selectedTextSet.language?.data?.attributes?.code && ( | ||||
|             <div className="grid place-items-center gap-2"> | ||||
|               <p className="font-headers">{langui.source_language}:</p> | ||||
|               <Button | ||||
|                 href={router.asPath} | ||||
|                 locale={ | ||||
|                   content.text_set[0].source_language.data.attributes.code | ||||
|                 } | ||||
|                 locale={selectedTextSet.source_language.data.attributes.code} | ||||
|               > | ||||
|                 {prettyLanguage( | ||||
|                   content.text_set[0].source_language.data.attributes.code, | ||||
|                   selectedTextSet.source_language.data.attributes.code, | ||||
|                   languages | ||||
|                 )} | ||||
|               </Button> | ||||
| @ -90,19 +132,19 @@ export default function Content(props: Props): JSX.Element { | ||||
|             <p className="font-headers">{langui.status}:</p> | ||||
| 
 | ||||
|             <ToolTip | ||||
|               content={getStatusDescription(content.text_set[0].status, langui)} | ||||
|               content={getStatusDescription(selectedTextSet.status, langui)} | ||||
|               maxWidth={"20rem"} | ||||
|             > | ||||
|               <Chip>{content.text_set[0].status}</Chip> | ||||
|               <Chip>{selectedTextSet.status}</Chip> | ||||
|             </ToolTip> | ||||
|           </div> | ||||
| 
 | ||||
|           {content.text_set[0].transcribers && | ||||
|             content.text_set[0].transcribers.data.length > 0 && ( | ||||
|           {selectedTextSet.transcribers && | ||||
|             selectedTextSet.transcribers.data.length > 0 && ( | ||||
|               <div> | ||||
|                 <p className="font-headers">{langui.transcribers}:</p> | ||||
|                 <div className="grid place-items-center place-content-center gap-2"> | ||||
|                   {content.text_set[0].transcribers.data.map((recorder) => ( | ||||
|                   {selectedTextSet.transcribers.data.map((recorder) => ( | ||||
|                     <> | ||||
|                       {recorder.attributes && ( | ||||
|                         <RecorderChip | ||||
| @ -117,12 +159,12 @@ export default function Content(props: Props): JSX.Element { | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|           {content.text_set[0].translators && | ||||
|             content.text_set[0].translators.data.length > 0 && ( | ||||
|           {selectedTextSet.translators && | ||||
|             selectedTextSet.translators.data.length > 0 && ( | ||||
|               <div> | ||||
|                 <p className="font-headers">{langui.translators}:</p> | ||||
|                 <div className="grid place-items-center place-content-center gap-2"> | ||||
|                   {content.text_set[0].translators.data.map((recorder) => ( | ||||
|                   {selectedTextSet.translators.data.map((recorder) => ( | ||||
|                     <> | ||||
|                       {recorder.attributes && ( | ||||
|                         <RecorderChip | ||||
| @ -137,12 +179,12 @@ export default function Content(props: Props): JSX.Element { | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|           {content.text_set[0].proofreaders && | ||||
|             content.text_set[0].proofreaders.data.length > 0 && ( | ||||
|           {selectedTextSet.proofreaders && | ||||
|             selectedTextSet.proofreaders.data.length > 0 && ( | ||||
|               <div> | ||||
|                 <p className="font-headers">{langui.proofreaders}:</p> | ||||
|                 <div className="grid place-items-center place-content-center gap-2"> | ||||
|                   {content.text_set[0].proofreaders.data.map((recorder) => ( | ||||
|                   {selectedTextSet.proofreaders.data.map((recorder) => ( | ||||
|                     <> | ||||
|                       {recorder.attributes && ( | ||||
|                         <RecorderChip | ||||
| @ -159,25 +201,23 @@ export default function Content(props: Props): JSX.Element { | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       {content?.text_set && | ||||
|         content.text_set.length > 0 && | ||||
|         content.text_set[0]?.text && ( | ||||
|           <> | ||||
|             <HorizontalLine /> | ||||
|             <TOC | ||||
|               text={content.text_set[0].text} | ||||
|               title={ | ||||
|                 content.titles && content.titles.length > 0 && content.titles[0] | ||||
|                   ? prettyinlineTitle( | ||||
|                       content.titles[0].pre_title, | ||||
|                       content.titles[0].title, | ||||
|                       content.titles[0].subtitle | ||||
|                     ) | ||||
|                   : prettySlug(content.slug) | ||||
|               } | ||||
|             /> | ||||
|           </> | ||||
|         )} | ||||
|       {selectedTextSet && content?.text_set && selectedTextSet.text && ( | ||||
|         <> | ||||
|           <HorizontalLine /> | ||||
|           <TOC | ||||
|             text={selectedTextSet.text} | ||||
|             title={ | ||||
|               content.titles && content.titles.length > 0 && selectedTitle | ||||
|                 ? prettyinlineTitle( | ||||
|                     selectedTitle.pre_title, | ||||
|                     selectedTitle.title, | ||||
|                     selectedTitle.subtitle | ||||
|                   ) | ||||
|                 : prettySlug(content.slug) | ||||
|             } | ||||
|           /> | ||||
|         </> | ||||
|       )} | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
| @ -189,46 +229,37 @@ export default function Content(props: Props): JSX.Element { | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
| 
 | ||||
|       {content && ( | ||||
|         <div className="grid place-items-center"> | ||||
|           <ThumbnailHeader | ||||
|             thumbnail={content.thumbnail?.data?.attributes} | ||||
|             pre_title={ | ||||
|               content.titles && content.titles.length > 0 | ||||
|                 ? content.titles[0]?.pre_title | ||||
|                 : undefined | ||||
|             } | ||||
|             title={ | ||||
|               content.titles && content.titles.length > 0 | ||||
|                 ? content.titles[0]?.title | ||||
|                 : prettySlug(content.slug) | ||||
|             } | ||||
|             subtitle={ | ||||
|               content.titles && content.titles.length > 0 | ||||
|                 ? content.titles[0]?.subtitle | ||||
|                 : undefined | ||||
|               selectedTitle?.pre_title ?? content.titles?.[0]?.pre_title | ||||
|             } | ||||
|             title={selectedTitle?.title ?? content.titles?.[0]?.title} | ||||
|             subtitle={selectedTitle?.subtitle ?? content.titles?.[0]?.subtitle} | ||||
|             description={ | ||||
|               content.titles && content.titles.length > 0 | ||||
|                 ? content.titles[0]?.description | ||||
|                 : undefined | ||||
|               selectedTitle?.description ?? content.titles?.[0]?.description | ||||
|             } | ||||
|             type={content.type} | ||||
|             categories={content.categories} | ||||
|             langui={langui} | ||||
|             languageSwitcher={ | ||||
|               selectedTextSet ? ( | ||||
|                 <LanguageSwitcher | ||||
|                   locales={textSetLocales} | ||||
|                   languages={props.languages} | ||||
|                   localesIndex={selectedTextSetIndex} | ||||
|                   setLocalesIndex={setSelectedTextSetIndex} | ||||
|                 /> | ||||
|               ) : undefined | ||||
|             } | ||||
|           /> | ||||
| 
 | ||||
|           <HorizontalLine /> | ||||
| 
 | ||||
|           {locales.includes(router.locale ?? "en") ? ( | ||||
|             <Markdawn text={content.text_set?.[0]?.text ?? ""} /> | ||||
|           ) : ( | ||||
|             <LanguageSwitcher | ||||
|               locales={locales} | ||||
|               languages={props.languages} | ||||
|               langui={props.langui} | ||||
|             /> | ||||
|           )} | ||||
|           <Markdawn text={selectedTextSet?.text ?? ""} /> | ||||
|         </div> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
| @ -276,7 +307,7 @@ export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = context.params?.slug?.toString() ?? ""; | ||||
|   const slug = context.params?.slug.toString() ?? ""; | ||||
|   const content = await sdk.getContentText({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|  | ||||
| @ -1,13 +1,8 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import Post from "components/Post"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { getLocalesFromLanguages, prettySlug } from "queries/helpers"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
| @ -17,35 +12,26 @@ interface Props extends AppStaticProps { | ||||
| } | ||||
| 
 | ||||
| export default function Home(props: Props): JSX.Element { | ||||
|   const { post } = props; | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
| 
 | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <div className="grid place-items-center place-content-center w-full gap-5 text-center"> | ||||
|         <div className="[mask:url('/icons/accords.svg')] [mask-size:contain] [mask-repeat:no-repeat] [mask-position:center] w-32 aspect-square mobile:w-[50vw] bg-black" /> | ||||
|         <h1 className="text-5xl mb-0">Accord’s Library</h1> | ||||
|         <h2 className="text-xl -mt-5"> | ||||
|           Discover • Analyze • Translate • Archive | ||||
|         </h2> | ||||
|       </div> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
|           languages={props.languages} | ||||
|           langui={props.langui} | ||||
|         /> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   const { post, langui, languages, currencies } = props; | ||||
|   return ( | ||||
|     <Post | ||||
|       currencies={currencies} | ||||
|       languages={languages} | ||||
|       langui={langui} | ||||
|       post={post} | ||||
|       prependBody={ | ||||
|         <div className="grid place-items-center place-content-center w-full gap-5 text-center"> | ||||
|           <div className="[mask:url('/icons/accords.svg')] [mask-size:contain] [mask-repeat:no-repeat] [mask-position:center] w-32 aspect-square mobile:w-[50vw] bg-black" /> | ||||
|           <h1 className="text-5xl mb-0">Accord’s Library</h1> | ||||
|           <h2 className="text-xl -mt-5"> | ||||
|             Discover • Analyze • Translate • Archive | ||||
|           </h2> | ||||
|         </div> | ||||
|       } | ||||
|       displayTitle={false} | ||||
|       displayLanguageSwitcher | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   return <AppLayout navTitle={title} contentPanel={contentPanel} {...props} />; | ||||
| } | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|  | ||||
| @ -1,17 +1,4 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import Chip from "components/Chip"; | ||||
| import ThumbnailHeader from "components/Content/ThumbnailHeader"; | ||||
| import HorizontalLine from "components/HorizontalLine"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import TOC from "components/Markdown/TOC"; | ||||
| import ReturnButton, { | ||||
|   ReturnButtonType, | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import RecorderChip from "components/RecorderChip"; | ||||
| import ToolTip from "components/ToolTip"; | ||||
| import Post from "components/Post"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { | ||||
| @ -19,13 +6,7 @@ import { | ||||
|   GetStaticPathsResult, | ||||
|   GetStaticPropsContext, | ||||
| } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { | ||||
|   getLocalesFromLanguages, | ||||
|   getStatusDescription, | ||||
|   prettySlug, | ||||
| } from "queries/helpers"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
| @ -39,106 +20,18 @@ interface Props extends AppStaticProps { | ||||
| } | ||||
| 
 | ||||
| export default function LibrarySlug(props: Props): JSX.Element { | ||||
|   const { post, langui } = props; | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const thumbnail = post?.translations?.[0]?.thumbnail?.data | ||||
|     ? post.translations[0].thumbnail.data.attributes | ||||
|     : post?.thumbnail?.data | ||||
|     ? post.thumbnail.data.attributes | ||||
|     : undefined; | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
|   const except = post?.translations?.[0]?.excerpt ?? ""; | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href="/news" | ||||
|         title={langui.news} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         horizontalLine | ||||
|       /> | ||||
| 
 | ||||
|       {post?.translations?.[0] && ( | ||||
|         <div className="grid grid-flow-col place-items-center place-content-center gap-2"> | ||||
|           <p className="font-headers">{langui.status}:</p> | ||||
| 
 | ||||
|           <ToolTip | ||||
|             content={getStatusDescription(post.translations[0].status, langui)} | ||||
|             maxWidth={"20rem"} | ||||
|           > | ||||
|             <Chip>{post.translations[0].status}</Chip> | ||||
|           </ToolTip> | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       {post?.authors && post.authors.data.length > 0 && ( | ||||
|         <div> | ||||
|           <p className="font-headers">{"Authors"}:</p> | ||||
|           <div className="grid place-items-center place-content-center gap-2"> | ||||
|             {post.authors.data.map((author) => ( | ||||
|               <> | ||||
|                 {author.attributes && ( | ||||
|                   <RecorderChip | ||||
|                     key={author.id} | ||||
|                     langui={langui} | ||||
|                     recorder={author.attributes} | ||||
|                   /> | ||||
|                 )} | ||||
|               </> | ||||
|             ))} | ||||
|           </div> | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href="/news" | ||||
|         title={langui.news} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
| 
 | ||||
|       <ThumbnailHeader | ||||
|         thumbnail={thumbnail} | ||||
|         title={title} | ||||
|         description={except} | ||||
|         langui={langui} | ||||
|         categories={post?.categories} | ||||
|       /> | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
|           languages={props.languages} | ||||
|           langui={props.langui} | ||||
|         /> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   const { post, langui, languages, currencies } = props; | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={title} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       thumbnail={thumbnail ?? undefined} | ||||
|       {...props} | ||||
|     <Post | ||||
|       currencies={currencies} | ||||
|       languages={languages} | ||||
|       langui={langui} | ||||
|       post={post} | ||||
|       returnHref="/news" | ||||
|       returnTitle={langui.news} | ||||
|       displayCredits | ||||
|       displayThumbnailHeader | ||||
|       displayToc | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| @ -480,3 +480,15 @@ export function arrayMove<T>(arr: T[], old_index: number, new_index: number) { | ||||
|   arr.splice(new_index, 0, arr.splice(old_index, 1)[0]); | ||||
|   return arr; | ||||
| } | ||||
| 
 | ||||
| export function getPreferredLanguage( | ||||
|   preferredLanguages: (string | undefined)[], | ||||
|   availableLanguages: Map<string, number> | ||||
| ): number | undefined { | ||||
|   for (const locale of preferredLanguages) { | ||||
|     if (locale && availableLanguages.has(locale)) { | ||||
|       return availableLanguages.get(locale); | ||||
|     } | ||||
|   } | ||||
|   return undefined; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint