Use graphql-codegen and replace all types from queries
This commit is contained in:
		
							parent
							
								
									acf92d6fc1
								
							
						
					
					
						commit
						125421de0f
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,6 +2,9 @@ | ||||
| 
 | ||||
| /testing_logs/* | ||||
| 
 | ||||
| # Generated content | ||||
| src/graphql/generated.ts | ||||
| 
 | ||||
| # dependencies | ||||
| /node_modules | ||||
| /.pnp | ||||
|  | ||||
| @ -25,9 +25,10 @@ | ||||
| #### [Front](https://github.com/Accords-Library/accords-library.com) (this repository) | ||||
| 
 | ||||
| - Language: [TypeScript](https://www.typescriptlang.org/) | ||||
| - Queries: [GraphQL](https://graphql.org/) | ||||
|   - [GraphQL Code Generator](https://www.graphql-code-generator.com/) to automatically generated types for the operations variables and responses | ||||
|   - The operations are stored in a graphql file and then retrieved and wrap as an actual TypeScript function | ||||
| - Queries: [GraphQL Code Generator](https://www.graphql-code-generator.com/) | ||||
|   - Fetch the GraphQL schema from the GraphQL back-end endpoint | ||||
|   - Read the operations and fragments stored as graphql files in the `src/graphql` folder | ||||
|   - Automatically generates a typesafe ready to use SDK using [graphql-request](https://www.npmjs.com/package/graphql-request) as the GraphQL client | ||||
| - Markdown: [markdown-to-jsx](https://www.npmjs.com/package/markdown-to-jsx) | ||||
|   - Support for Arbitrary React Components and Component Props! | ||||
|   - Autogenerated multi-level table of content and anchor links for the different headers | ||||
|  | ||||
							
								
								
									
										21
									
								
								graphql-codegen.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								graphql-codegen.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| const { loadEnvConfig } = require("@next/env"); | ||||
| loadEnvConfig(process.cwd()); | ||||
| 
 | ||||
| module.exports = { | ||||
|   overwrite: true, | ||||
|   schema: { | ||||
|     [process.env.URL_GRAPHQL]: { | ||||
|       headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` }, | ||||
|     }, | ||||
|   }, | ||||
|   documents: ["src/graphql/operations/*.graphql", "src/graphql/fragments/*.graphql"], | ||||
|   generates: { | ||||
|     "src/graphql/generated.ts": { | ||||
|       plugins: [ | ||||
|         "typescript", | ||||
|         "typescript-operations", | ||||
|         "typescript-graphql-request", | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
							
								
								
									
										9015
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9015
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -6,7 +6,8 @@ | ||||
|     "build": "next build", | ||||
|     "postbuild": "next-sitemap", | ||||
|     "start": "next start", | ||||
|     "lint": "next lint" | ||||
|     "lint": "next lint", | ||||
|     "generate": "graphql-codegen --config graphql-codegen.js" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@fontsource/material-icons": "^4.5.4", | ||||
| @ -15,6 +16,7 @@ | ||||
|     "@fontsource/vollkorn": "^4.5.6", | ||||
|     "@fontsource/zen-maru-gothic": "^4.5.8", | ||||
|     "@tippyjs/react": "^4.2.6", | ||||
|     "graphql-request": "^4.2.0", | ||||
|     "markdown-to-jsx": "^7.1.7", | ||||
|     "next": "^12.1.2", | ||||
|     "nodemailer": "^6.7.3", | ||||
| @ -25,6 +27,10 @@ | ||||
|     "turndown": "^7.1.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@graphql-codegen/cli": "^2.6.2", | ||||
|     "@graphql-codegen/typescript": "2.4.8", | ||||
|     "@graphql-codegen/typescript-graphql-request": "^4.4.4", | ||||
|     "@graphql-codegen/typescript-operations": "^2.3.5", | ||||
|     "@types/node": "17.0.23", | ||||
|     "@types/nodemailer": "^6.4.4", | ||||
|     "@types/react": "17.0.43", | ||||
| @ -34,6 +40,7 @@ | ||||
|     "@typescript-eslint/parser": "^5.17.0", | ||||
|     "eslint": "^8.12.0", | ||||
|     "eslint-config-next": "12.1.2", | ||||
|     "graphql": "^14.7.0", | ||||
|     "next-sitemap": "^2.5.14", | ||||
|     "prettier-plugin-organize-imports": "^2.3.4", | ||||
|     "tailwindcss": "^3.0.23", | ||||
|  | ||||
| @ -1 +1,3 @@ | ||||
| npx next build | ||||
| npm run generate | ||||
| npm run build | ||||
| npm run postbuild | ||||
| @ -1,11 +1,16 @@ | ||||
| import Button from "components/Button"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { StrapiImage } from "graphql/operations-types"; | ||||
| import { UploadImageFragment } from "graphql/generated"; | ||||
| import { useMediaMobile } from "hooks/useMediaQuery"; | ||||
| import Head from "next/head"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { getOgImage, OgImage, prettyLanguage } from "queries/helpers"; | ||||
| import { | ||||
|   getOgImage, | ||||
|   OgImage, | ||||
|   prettyLanguage, | ||||
|   prettySlug, | ||||
| } from "queries/helpers"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import { useSwipeable } from "react-swipeable"; | ||||
| import { ImageQuality } from "./Img"; | ||||
| @ -18,8 +23,8 @@ interface AppLayoutProps extends AppStaticProps { | ||||
|   subPanelIcon?: string; | ||||
|   contentPanel?: React.ReactNode; | ||||
|   title?: string; | ||||
|   navTitle: string; | ||||
|   thumbnail?: StrapiImage; | ||||
|   navTitle: string | null | undefined; | ||||
|   thumbnail?: UploadImageFragment; | ||||
|   description?: string; | ||||
| } | ||||
| 
 | ||||
| @ -61,11 +66,10 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { | ||||
|         height: 630, | ||||
|         alt: "Accord's Library Logo", | ||||
|       }; | ||||
|   const ogTitle = props.title ? props.title : props.navTitle; | ||||
|   const ogTitle = | ||||
|     props.title ?? props.navTitle ?? prettySlug(router.asPath.split("/").pop()); | ||||
| 
 | ||||
|   const metaDescription = props.description | ||||
|     ? props.description | ||||
|     : langui.default_description; | ||||
|   const metaDescription = props.description ?? langui.default_description ?? ""; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     document.getElementsByTagName("html")[0].style.fontSize = `${ | ||||
| @ -73,9 +77,10 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { | ||||
|     }%`;
 | ||||
|   }, [appLayout.fontSize]); | ||||
| 
 | ||||
|   const currencyOptions = currencies.map( | ||||
|     (currency) => currency.attributes.code | ||||
|   ); | ||||
|   const currencyOptions: string[] = []; | ||||
|   currencies.map((currency) => { | ||||
|     if (currency.attributes?.code) currencyOptions.push(currency.attributes.code); | ||||
|   }); | ||||
|   const [currencySelect, setCurrencySelect] = useState<number>(-1); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
| @ -127,7 +132,10 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { | ||||
|           ></meta> | ||||
| 
 | ||||
|           <meta name="description" content={metaDescription} /> | ||||
|           <meta name="twitter:description" content={metaDescription}></meta> | ||||
|           <meta | ||||
|             name="twitter:description" | ||||
|             content={metaDescription} | ||||
|           ></meta> | ||||
| 
 | ||||
|           <meta property="og:image" content={metaImage.image}></meta> | ||||
|           <meta property="og:image:secure_url" content={metaImage.image}></meta> | ||||
| @ -220,12 +228,12 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element { | ||||
|           </span> | ||||
|           <p | ||||
|             className={`font-black font-headers text-center overflow-hidden ${ | ||||
|               props.navTitle?.length > 30 | ||||
|               ogTitle && ogTitle.length > 30 | ||||
|                 ? "text-xl max-h-14" | ||||
|                 : "text-2xl max-h-16" | ||||
|             }`}
 | ||||
|           > | ||||
|             {props.navTitle} | ||||
|             {ogTitle} | ||||
|           </p> | ||||
|           <span | ||||
|             className="material-icons mt-[.1em] cursor-pointer" | ||||
|  | ||||
| @ -3,14 +3,17 @@ import ToolTip from "components/ToolTip"; | ||||
| import { | ||||
|   Enum_Componenttranslationschronologyitem_Status, | ||||
|   GetChronologyItemsQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| } from "graphql/generated"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { getStatusDescription } from "queries/helpers"; | ||||
| 
 | ||||
| export type ChronologyItemComponentProps = { | ||||
|   item: GetChronologyItemsQuery["chronologyItems"]["data"][number]; | ||||
|   item: Exclude< | ||||
|     GetChronologyItemsQuery["chronologyItems"], | ||||
|     null | undefined | ||||
|   >["data"][number]; | ||||
|   displayYear: boolean; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
| }; | ||||
| 
 | ||||
| export default function ChronologyItemComponent( | ||||
| @ -18,22 +21,29 @@ export default function ChronologyItemComponent( | ||||
| ): JSX.Element { | ||||
|   const { langui } = props; | ||||
| 
 | ||||
|   function generateAnchor(year: number, month: number, day: number): string { | ||||
|   function generateAnchor( | ||||
|     year: number | undefined, | ||||
|     month: number | null | undefined, | ||||
|     day: number | null | undefined | ||||
|   ): string { | ||||
|     let result = ""; | ||||
|     result += year; | ||||
|     if (year) result += year; | ||||
|     if (month) result += `- ${month.toString().padStart(2, "0")}`; | ||||
|     if (day) result += `- ${day.toString().padStart(2, "0")}`; | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   function generateYear(displayed_date: string, year: number): string { | ||||
|     if (displayed_date) { | ||||
|       return displayed_date; | ||||
|     } | ||||
|     return year.toString(); | ||||
|   function generateYear( | ||||
|     displayed_date: string | null | undefined, | ||||
|     year: number | undefined | ||||
|   ): string { | ||||
|     return displayed_date ?? year?.toString() ?? ""; | ||||
|   } | ||||
| 
 | ||||
|   function generateDate(month: number, day: number): string { | ||||
|   function generateDate( | ||||
|     month: number | null | undefined, | ||||
|     day: number | null | undefined | ||||
|   ): string { | ||||
|     const lut = [ | ||||
|       "Jan", | ||||
|       "Feb", | ||||
| @ -50,7 +60,7 @@ export default function ChronologyItemComponent( | ||||
|     ]; | ||||
| 
 | ||||
|     let result = ""; | ||||
|     if (month) { | ||||
|     if (month && month >= 1 && month <= 12) { | ||||
|       result += lut[month - 1]; | ||||
|       if (day) { | ||||
|         result += ` ${day}`; | ||||
| @ -60,78 +70,98 @@ export default function ChronologyItemComponent( | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className="grid place-content-start grid-rows-[auto_1fr] grid-cols-[4em] py-4 px-8 rounded-2xl target:bg-mid target:py-8 target:my-4" | ||||
|       id={generateAnchor( | ||||
|         props.item.attributes.year, | ||||
|         props.item.attributes.month, | ||||
|         props.item.attributes.day | ||||
|       )} | ||||
|     > | ||||
|       {props.displayYear && ( | ||||
|         <p className="text-lg mt-[-.2em] font-bold"> | ||||
|           {generateYear( | ||||
|             props.item.attributes.displayed_date, | ||||
|             props.item.attributes.year | ||||
|           )} | ||||
|   if (props.item.attributes) { | ||||
|     return ( | ||||
|       <div | ||||
|         className="grid place-content-start grid-rows-[auto_1fr] grid-cols-[4em] py-4 px-8 rounded-2xl target:bg-mid target:py-8 target:my-4" | ||||
|         id={generateAnchor( | ||||
|           props.item.attributes.year, | ||||
|           props.item.attributes.month, | ||||
|           props.item.attributes.day | ||||
|         )} | ||||
|       > | ||||
|         {props.displayYear && ( | ||||
|           <p className="text-lg mt-[-.2em] font-bold"> | ||||
|             {generateYear( | ||||
|               props.item.attributes.displayed_date, | ||||
|               props.item.attributes.year | ||||
|             )} | ||||
|           </p> | ||||
|         )} | ||||
| 
 | ||||
|         <p className="col-start-1 text-dark text-sm"> | ||||
|           {generateDate(props.item.attributes.month, props.item.attributes.day)} | ||||
|         </p> | ||||
|       )} | ||||
| 
 | ||||
|       <p className="col-start-1 text-dark text-sm"> | ||||
|         {generateDate(props.item.attributes.month, props.item.attributes.day)} | ||||
|       </p> | ||||
|         <div className="col-start-2 row-start-1 row-span-2 grid gap-4"> | ||||
|           {props.item.attributes.events?.map((event) => ( | ||||
|             <> | ||||
|               {event && ( | ||||
|                 <div className="m-0" key={event.id}> | ||||
|                   {event.translations?.map((translation) => ( | ||||
|                     <> | ||||
|                       {translation && ( | ||||
|                         <> | ||||
|                           <div className="place-items-start place-content-start grid grid-flow-col gap-2"> | ||||
|                             {translation.status !== | ||||
|                               Enum_Componenttranslationschronologyitem_Status.Done && ( | ||||
|                               <ToolTip | ||||
|                                 content={getStatusDescription( | ||||
|                                   translation.status, | ||||
|                                   langui | ||||
|                                 )} | ||||
|                                 maxWidth={"20rem"} | ||||
|                               > | ||||
|                                 <Chip>{translation.status}</Chip> | ||||
|                               </ToolTip> | ||||
|                             )} | ||||
|                             {translation.title ? ( | ||||
|                               <h3>{translation.title}</h3> | ||||
|                             ) : ( | ||||
|                               "" | ||||
|                             )} | ||||
|                           </div> | ||||
| 
 | ||||
|       <div className="col-start-2 row-start-1 row-span-2 grid gap-4"> | ||||
|         {props.item.attributes.events.map((event) => ( | ||||
|           <div className="m-0" key={event.id}> | ||||
|             {event.translations.map((translation) => ( | ||||
|               <> | ||||
|                 <div className="place-items-start place-content-start grid grid-flow-col gap-2"> | ||||
|                   {translation.status !== | ||||
|                     Enum_Componenttranslationschronologyitem_Status.Done && ( | ||||
|                     <ToolTip | ||||
|                       content={getStatusDescription(translation.status, langui)} | ||||
|                       maxWidth={"20rem"} | ||||
|                     > | ||||
|                       <Chip>{translation.status}</Chip> | ||||
|                     </ToolTip> | ||||
|                   )} | ||||
|                   {translation.title ? <h3>{translation.title}</h3> : ""} | ||||
|                 </div> | ||||
|                           {translation.description && ( | ||||
|                             <p | ||||
|                               className={ | ||||
|                                 event.translations && | ||||
|                                 event.translations.length > 1 | ||||
|                                   ? "before:content-['-'] before:text-dark before:inline-block before:w-4 before:ml-[-1em] mt-2 whitespace-pre-line" | ||||
|                                   : "whitespace-pre-line" | ||||
|                               } | ||||
|                             > | ||||
|                               {translation.description} | ||||
|                             </p> | ||||
|                           )} | ||||
|                           {translation.note ? ( | ||||
|                             <em>{`Notes: ${translation.note}`}</em> | ||||
|                           ) : ( | ||||
|                             "" | ||||
|                           )} | ||||
|                         </> | ||||
|                       )} | ||||
|                     </> | ||||
|                   ))} | ||||
| 
 | ||||
|                 {translation.description && ( | ||||
|                   <p | ||||
|                     className={ | ||||
|                       event.translations.length > 1 | ||||
|                         ? "before:content-['-'] before:text-dark before:inline-block before:w-4 before:ml-[-1em] mt-2 whitespace-pre-line" | ||||
|                         : "whitespace-pre-line" | ||||
|                     } | ||||
|                   > | ||||
|                     {translation.description} | ||||
|                   <p className="text-dark text-xs grid place-self-start grid-flow-col gap-1 mt-1"> | ||||
|                     {event.source?.data ? ( | ||||
|                       `(${event.source.data.attributes?.name})` | ||||
|                     ) : ( | ||||
|                       <> | ||||
|                         <span className="material-icons !text-sm">warning</span> | ||||
|                         No sources! | ||||
|                       </> | ||||
|                     )} | ||||
|                   </p> | ||||
|                 )} | ||||
|                 {translation.note ? ( | ||||
|                   <em>{`Notes: ${translation.note}`}</em> | ||||
|                 ) : ( | ||||
|                   "" | ||||
|                 )} | ||||
|               </> | ||||
|             ))} | ||||
| 
 | ||||
|             <p className="text-dark text-xs grid place-self-start grid-flow-col gap-1 mt-1"> | ||||
|               {event.source.data ? ( | ||||
|                 `(${event.source.data.attributes.name})` | ||||
|               ) : ( | ||||
|                 <> | ||||
|                   <span className="material-icons !text-sm">warning</span>No | ||||
|                   sources! | ||||
|                 </> | ||||
|                 </div> | ||||
|               )} | ||||
|             </p> | ||||
|           </div> | ||||
|         ))} | ||||
|             </> | ||||
|           ))} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   return <></>; | ||||
| } | ||||
|  | ||||
| @ -1,13 +1,14 @@ | ||||
| import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent"; | ||||
| import { | ||||
|   GetChronologyItemsQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { GetChronologyItemsQuery } from "graphql/generated"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| 
 | ||||
| type ChronologyYearComponentProps = { | ||||
|   year: number; | ||||
|   items: GetChronologyItemsQuery["chronologyItems"]["data"][number][]; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   items: Exclude< | ||||
|     GetChronologyItemsQuery["chronologyItems"], | ||||
|     null | undefined | ||||
|   >["data"][number][]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
| }; | ||||
| 
 | ||||
| export default function ChronologyYearComponent( | ||||
|  | ||||
| @ -1,21 +1,31 @@ | ||||
| import Chip from "components/Chip"; | ||||
| import Img, { ImageQuality } from "components/Img"; | ||||
| import InsetBox from "components/InsetBox"; | ||||
| import { | ||||
|   GetContentQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { GetContentQuery, UploadImageFragment } from "graphql/generated"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyinlineTitle, prettySlug, slugify } from "queries/helpers"; | ||||
| 
 | ||||
| export type ThumbnailHeaderProps = { | ||||
|   pre_title?: string; | ||||
|   title: string; | ||||
|   subtitle?: string; | ||||
|   description?: string; | ||||
|   type?: GetContentQuery["contents"]["data"][number]["attributes"]["type"]; | ||||
|   categories?: GetContentQuery["contents"]["data"][number]["attributes"]["categories"]; | ||||
|   thumbnail?: GetContentQuery["contents"]["data"][number]["attributes"]["thumbnail"]["data"]["attributes"]; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   pre_title?: string | null | undefined; | ||||
|   title: string | null | undefined; | ||||
|   subtitle?: string | null | undefined; | ||||
|   description?: string | null | undefined; | ||||
|   type?: Exclude< | ||||
|     Exclude< | ||||
|       GetContentQuery["contents"], | ||||
|       null | undefined | ||||
|     >["data"][number]["attributes"], | ||||
|     null | undefined | ||||
|   >["type"]; | ||||
|   categories?: Exclude< | ||||
|     Exclude< | ||||
|       GetContentQuery["contents"], | ||||
|       null | undefined | ||||
|     >["data"][number]["attributes"], | ||||
|     null | undefined | ||||
|   >["categories"]; | ||||
|   thumbnail?: UploadImageFragment | null | undefined; | ||||
|   langui: AppStaticProps["langui"]; | ||||
| }; | ||||
| 
 | ||||
| export default function ThumbnailHeader( | ||||
| @ -60,13 +70,14 @@ export default function ThumbnailHeader( | ||||
|       </div> | ||||
| 
 | ||||
|       <div className="grid grid-flow-col gap-8"> | ||||
|         {type?.data && ( | ||||
|         {type?.data?.attributes && ( | ||||
|           <div className="flex flex-col place-items-center gap-2"> | ||||
|             <h3 className="text-xl">{langui.type}</h3> | ||||
|             <div className="flex flex-row flex-wrap"> | ||||
|               <Chip> | ||||
|                 {type.data.attributes.titles.length > 0 | ||||
|                   ? type.data.attributes.titles[0].title | ||||
|                 {type.data.attributes.titles && | ||||
|                 type.data.attributes.titles.length > 0 | ||||
|                   ? type.data.attributes.titles[0]?.title | ||||
|                   : prettySlug(type.data.attributes.slug)} | ||||
|               </Chip> | ||||
|             </div> | ||||
| @ -78,7 +89,7 @@ export default function ThumbnailHeader( | ||||
|             <h3 className="text-xl">{langui.categories}</h3> | ||||
|             <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|               {categories.data.map((category) => ( | ||||
|                 <Chip key={category.id}>{category.attributes.name}</Chip> | ||||
|                 <Chip key={category.id}>{category.attributes?.name}</Chip> | ||||
|               ))} | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { StrapiImage } from "graphql/operations-types"; | ||||
| import { UploadImageFragment } from "graphql/generated"; | ||||
| import Image, { ImageProps } from "next/image"; | ||||
| 
 | ||||
| export enum ImageQuality { | ||||
| @ -51,7 +51,7 @@ export function getImgSizesByQuality( | ||||
| 
 | ||||
| type ImgProps = { | ||||
|   className?: string; | ||||
|   image?: StrapiImage; | ||||
|   image?: UploadImageFragment; | ||||
|   quality?: ImageQuality; | ||||
|   alt?: ImageProps["alt"]; | ||||
|   layout?: ImageProps["layout"]; | ||||
| @ -61,11 +61,11 @@ type ImgProps = { | ||||
| }; | ||||
| 
 | ||||
| export default function Img(props: ImgProps): JSX.Element { | ||||
|   if (props.image) { | ||||
|   if (props.image?.width && props.image?.height) { | ||||
|     const imgSize = getImgSizesByQuality( | ||||
|       props.image.width, | ||||
|       props.image.height, | ||||
|       props.quality ? props.quality : ImageQuality.Small | ||||
|       props.quality ?? ImageQuality.Small | ||||
|     ); | ||||
| 
 | ||||
|     if (props.rawImg) { | ||||
| @ -75,9 +75,9 @@ export default function Img(props: ImgProps): JSX.Element { | ||||
|           className={props.className} | ||||
|           src={getAssetURL( | ||||
|             props.image.url, | ||||
|             props.quality ? props.quality : ImageQuality.Small | ||||
|             props.quality ?? ImageQuality.Small | ||||
|           )} | ||||
|           alt={props.alt ? props.alt : props.image.alternativeText} | ||||
|           alt={props.alt ?? props.image.alternativeText ?? ""} | ||||
|           width={imgSize.width} | ||||
|           height={imgSize.height} | ||||
|         /> | ||||
| @ -90,7 +90,7 @@ export default function Img(props: ImgProps): JSX.Element { | ||||
|           props.image.url, | ||||
|           props.quality ? props.quality : ImageQuality.Small | ||||
|         )} | ||||
|         alt={props.alt ? props.alt : props.image.alternativeText} | ||||
|         alt={props.alt ?? props.image.alternativeText ?? ""} | ||||
|         width={props.layout === "fill" ? undefined : imgSize.width} | ||||
|         height={props.layout === "fill" ? undefined : imgSize.height} | ||||
|         layout={props.layout} | ||||
|  | ||||
| @ -1,16 +1,13 @@ | ||||
| import { | ||||
|   GetLanguagesQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyLanguage } from "queries/helpers"; | ||||
| import Button from "./Button"; | ||||
| 
 | ||||
| type HorizontalLineProps = { | ||||
|   className?: string; | ||||
|   locales: string[]; | ||||
|   languages: GetLanguagesQuery["languages"]["data"]; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   locales: (string | undefined)[]; | ||||
|   languages: AppStaticProps["languages"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   href?: string; | ||||
| }; | ||||
| 
 | ||||
| @ -26,14 +23,18 @@ export default function HorizontalLine( | ||||
|         <p>{langui.language_switch_message}</p> | ||||
|         <div className="flex flex-wrap flex-row gap-2"> | ||||
|           {locales.map((locale, index) => ( | ||||
|             <Button | ||||
|               key={index} | ||||
|               active={locale === router.locale} | ||||
|               href={href} | ||||
|               locale={locale} | ||||
|             > | ||||
|               {prettyLanguage(locale, props.languages)} | ||||
|             </Button> | ||||
|             <> | ||||
|               {locale && ( | ||||
|                 <Button | ||||
|                   key={index} | ||||
|                   active={locale === router.locale} | ||||
|                   href={href} | ||||
|                   locale={locale} | ||||
|                 > | ||||
|                   {prettyLanguage(locale, props.languages)} | ||||
|                 </Button> | ||||
|               )} | ||||
|             </> | ||||
|           ))} | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
| @ -1,16 +1,23 @@ | ||||
| import Button from "components/Button"; | ||||
| import Chip from "components/Chip"; | ||||
| import { | ||||
|   GetLibraryItemQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { GetLibraryItemQuery } from "graphql/generated"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyinlineTitle, prettySlug } from "queries/helpers"; | ||||
| import { useState } from "react"; | ||||
| 
 | ||||
| type ContentTOCLineProps = { | ||||
|   content: GetLibraryItemQuery["libraryItems"]["data"][number]["attributes"]["contents"]["data"][number]; | ||||
|   content: Exclude< | ||||
|     Exclude< | ||||
|       Exclude< | ||||
|         GetLibraryItemQuery["libraryItems"], | ||||
|         null | undefined | ||||
|       >["data"][number]["attributes"], | ||||
|       null | undefined | ||||
|     >["contents"], | ||||
|     null | undefined | ||||
|   >["data"][number]; | ||||
|   parentSlug: string; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
| }; | ||||
| 
 | ||||
| export default function ContentTOCLine( | ||||
| @ -20,82 +27,91 @@ export default function ContentTOCLine( | ||||
| 
 | ||||
|   const [opened, setOpened] = useState(false); | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={`grid gap-2 px-4 rounded-lg ${ | ||||
|         opened && "bg-mid shadow-inner-sm shadow-shade h-auto py-3 my-2" | ||||
|       }`}
 | ||||
|     > | ||||
|       <div className="grid gap-4 place-items-center grid-cols-[auto_auto_1fr_auto_12ch] thin:grid-cols-[auto_auto_1fr_auto]"> | ||||
|         <a> | ||||
|           <h3 className="cursor-pointer" onClick={() => setOpened(!opened)}> | ||||
|             {content.attributes.content.data && | ||||
|             content.attributes.content.data.attributes.titles.length > 0 | ||||
|               ? prettyinlineTitle( | ||||
|                   content.attributes.content.data.attributes.titles[0] | ||||
|                     .pre_title, | ||||
|                   content.attributes.content.data.attributes.titles[0].title, | ||||
|                   content.attributes.content.data.attributes.titles[0].subtitle | ||||
|                 ) | ||||
|               : prettySlug(content.attributes.slug, props.parentSlug)} | ||||
|           </h3> | ||||
|         </a> | ||||
|         <div className="flex flex-row flex-wrap gap-1"> | ||||
|           {content.attributes.content.data?.attributes.categories.data.map( | ||||
|             (category) => ( | ||||
|               <Chip key={category.id}>{category.attributes.short}</Chip> | ||||
|             ) | ||||
|           )} | ||||
|         </div> | ||||
|         <p className="border-b-2 h-4 w-full border-black border-dotted opacity-30"></p> | ||||
|         <p> | ||||
|           {content.attributes.range[0].__typename === "ComponentRangePageRange" | ||||
|             ? content.attributes.range[0].starting_page | ||||
|             : ""} | ||||
|         </p> | ||||
|         {content.attributes.content.data && ( | ||||
|           <Chip className="justify-self-end thin:hidden"> | ||||
|             {content.attributes.content.data.attributes.type.data.attributes | ||||
|               .titles.length > 0 | ||||
|               ? content.attributes.content.data.attributes.type.data.attributes | ||||
|                   .titles[0].title | ||||
|               : prettySlug( | ||||
|                   content.attributes.content.data.attributes.type.data | ||||
|                     .attributes.slug | ||||
|                 )} | ||||
|           </Chip> | ||||
|         )} | ||||
|       </div> | ||||
|   if (content.attributes) { | ||||
|     return ( | ||||
|       <div | ||||
|         className={`grid-flow-col place-content-start place-items-center gap-2 ${ | ||||
|           opened ? "grid" : "hidden" | ||||
|         className={`grid gap-2 px-4 rounded-lg ${ | ||||
|           opened && "bg-mid shadow-inner-sm shadow-shade h-auto py-3 my-2" | ||||
|         }`}
 | ||||
|       > | ||||
|         <span className="material-icons text-dark"> | ||||
|           subdirectory_arrow_right | ||||
|         </span> | ||||
|         <div className="grid gap-4 place-items-center grid-cols-[auto_auto_1fr_auto_12ch] thin:grid-cols-[auto_auto_1fr_auto]"> | ||||
|           <a> | ||||
|             <h3 className="cursor-pointer" onClick={() => setOpened(!opened)}> | ||||
|               {content.attributes.content?.data?.attributes?.titles?.[0] | ||||
|                 ? prettyinlineTitle( | ||||
|                     content.attributes.content.data.attributes.titles[0] | ||||
|                       ?.pre_title, | ||||
|                     content.attributes.content.data.attributes.titles[0]?.title, | ||||
|                     content.attributes.content.data.attributes.titles[0] | ||||
|                       ?.subtitle | ||||
|                   ) | ||||
|                 : prettySlug(content.attributes.slug, props.parentSlug)} | ||||
|             </h3> | ||||
|           </a> | ||||
|           <div className="flex flex-row flex-wrap gap-1"> | ||||
|             {content.attributes.content?.data?.attributes?.categories?.data.map( | ||||
|               (category) => ( | ||||
|                 <Chip key={category.id}>{category.attributes?.short}</Chip> | ||||
|               ) | ||||
|             )} | ||||
|           </div> | ||||
|           <p className="border-b-2 h-4 w-full border-black border-dotted opacity-30"></p> | ||||
|           <p> | ||||
|             {content.attributes.range[0]?.__typename === | ||||
|             "ComponentRangePageRange" | ||||
|               ? content.attributes.range[0].starting_page | ||||
|               : ""} | ||||
|           </p> | ||||
|           {content.attributes.content?.data?.attributes?.type?.data | ||||
|             ?.attributes && ( | ||||
|             <Chip className="justify-self-end thin:hidden"> | ||||
|               {content.attributes.content.data.attributes.type.data.attributes | ||||
|                 .titles && | ||||
|               content.attributes.content.data.attributes.type.data.attributes | ||||
|                 .titles.length > 0 | ||||
|                 ? content.attributes.content.data.attributes.type.data | ||||
|                     .attributes.titles[0]?.title | ||||
|                 : prettySlug( | ||||
|                     content.attributes.content.data.attributes.type.data | ||||
|                       .attributes.slug | ||||
|                   )} | ||||
|             </Chip> | ||||
|           )} | ||||
|         </div> | ||||
|         <div | ||||
|           className={`grid-flow-col place-content-start place-items-center gap-2 ${ | ||||
|             opened ? "grid" : "hidden" | ||||
|           }`}
 | ||||
|         > | ||||
|           <span className="material-icons text-dark"> | ||||
|             subdirectory_arrow_right | ||||
|           </span> | ||||
| 
 | ||||
|         {content.attributes.scan_set.length > 0 && ( | ||||
|           <Button | ||||
|             href={`/library/${parentSlug}/scans#${content.attributes.slug}`} | ||||
|           > | ||||
|             {langui.view_scans} | ||||
|           </Button> | ||||
|         )} | ||||
|           {content.attributes.scan_set && | ||||
|             content.attributes.scan_set.length > 0 && ( | ||||
|               <Button | ||||
|                 href={`/library/${parentSlug}/scans#${content.attributes.slug}`} | ||||
|               > | ||||
|                 {langui.view_scans} | ||||
|               </Button> | ||||
|             )} | ||||
| 
 | ||||
|         {content.attributes.content.data && ( | ||||
|           <Button | ||||
|             href={`/contents/${content.attributes.content.data.attributes.slug}`} | ||||
|           > | ||||
|             {langui.open_content} | ||||
|           </Button> | ||||
|         )} | ||||
|           {content.attributes.content?.data && ( | ||||
|             <Button | ||||
|               href={`/contents/${content.attributes.content.data.attributes?.slug}`} | ||||
|             > | ||||
|               {langui.open_content} | ||||
|             </Button> | ||||
|           )} | ||||
| 
 | ||||
|         {content.attributes.scan_set.length === 0 && | ||||
|         !content.attributes.content.data | ||||
|           ? "The content is not available" | ||||
|           : ""} | ||||
|           {content.attributes.scan_set && | ||||
|           content.attributes.scan_set.length === 0 && | ||||
|           !content.attributes.content?.data | ||||
|             ? "The content is not available" | ||||
|             : ""} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
|     ); | ||||
|   } | ||||
|   return <></>; | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,14 @@ | ||||
| import Chip from "components/Chip"; | ||||
| import Img, { ImageQuality } from "components/Img"; | ||||
| import { GetContentsQuery } from "graphql/operations-types"; | ||||
| import { GetContentsQuery } from "graphql/generated"; | ||||
| import Link from "next/link"; | ||||
| import { prettySlug } from "queries/helpers"; | ||||
| 
 | ||||
| export type LibraryContentPreviewProps = { | ||||
|   item: { | ||||
|     slug: GetContentsQuery["contents"]["data"][number]["attributes"]["slug"]; | ||||
|     thumbnail: GetContentsQuery["contents"]["data"][number]["attributes"]["thumbnail"]; | ||||
|     titles: GetContentsQuery["contents"]["data"][number]["attributes"]["titles"]; | ||||
|     categories: GetContentsQuery["contents"]["data"][number]["attributes"]["categories"]; | ||||
|     type: GetContentsQuery["contents"]["data"][number]["attributes"]["type"]; | ||||
|   }; | ||||
|   item: Exclude< | ||||
|     GetContentsQuery["contents"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| }; | ||||
| 
 | ||||
| export default function LibraryContentPreview( | ||||
| @ -20,9 +17,9 @@ export default function LibraryContentPreview( | ||||
|   const { item } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <Link href={`/contents/${item.slug}`} passHref> | ||||
|     <Link href={`/contents/${item?.slug}`} passHref> | ||||
|       <div className="drop-shadow-shade-xl cursor-pointer grid items-end fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform"> | ||||
|         {item.thumbnail.data ? ( | ||||
|         {item?.thumbnail?.data?.attributes ? ( | ||||
|           <Img | ||||
|             className="rounded-md coarse:rounded-b-none" | ||||
|             image={item.thumbnail.data.attributes} | ||||
| @ -33,29 +30,29 @@ export default function LibraryContentPreview( | ||||
|         )} | ||||
|         <div className="linearbg-obi fine:drop-shadow-shade-lg fine:absolute coarse:rounded-b-md bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2"> | ||||
|           <div className="grid grid-flow-col gap-1 overflow-hidden place-content-start"> | ||||
|             {item.type.data && ( | ||||
|             {item?.type?.data?.attributes && ( | ||||
|               <Chip> | ||||
|                 {item.type.data.attributes.titles.length > 0 | ||||
|                   ? item.type.data.attributes.titles[0].title | ||||
|                 {item.type.data.attributes.titles?.[0] | ||||
|                   ? item.type.data.attributes.titles[0]?.title | ||||
|                   : prettySlug(item.type.data.attributes.slug)} | ||||
|               </Chip> | ||||
|             )} | ||||
|           </div> | ||||
|           <div> | ||||
|             {item.titles.length > 0 ? ( | ||||
|             {item?.titles?.[0] ? ( | ||||
|               <> | ||||
|                 <p>{item.titles[0].pre_title}</p> | ||||
|                 <h1 className="text-lg">{item.titles[0].title}</h1> | ||||
|                 <h2>{item.titles[0].subtitle}</h2> | ||||
|               </> | ||||
|             ) : ( | ||||
|               <h1 className="text-lg">{prettySlug(item.slug)}</h1> | ||||
|               <h1 className="text-lg">{prettySlug(item?.slug)}</h1> | ||||
|             )} | ||||
|           </div> | ||||
|           <div className="grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:w-0 [scrollbar-width:none] place-content-start"> | ||||
|             {item.categories.data.map((category) => ( | ||||
|             {item?.categories?.data.map((category) => ( | ||||
|               <Chip key={category.id} className="text-sm"> | ||||
|                 {category.attributes.short} | ||||
|                 {category.attributes?.short} | ||||
|               </Chip> | ||||
|             ))} | ||||
|           </div> | ||||
|  | ||||
| @ -2,25 +2,31 @@ import Chip from "components/Chip"; | ||||
| import Img, { ImageQuality } from "components/Img"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { | ||||
|   GetCurrenciesQuery, | ||||
|   GetLibraryItemQuery, | ||||
|   GetLibraryItemsPreviewQuery, | ||||
| } from "graphql/operations-types"; | ||||
| } from "graphql/generated"; | ||||
| import Link from "next/link"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyDate, prettyItemSubType, prettyPrice } from "queries/helpers"; | ||||
| 
 | ||||
| export type LibraryItemsPreviewProps = { | ||||
|   className?: string; | ||||
|   item: { | ||||
|     slug: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["slug"]; | ||||
|     thumbnail: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["thumbnail"]; | ||||
|     title: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["title"]; | ||||
|     subtitle: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["subtitle"]; | ||||
|     price?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["price"]; | ||||
|     categories: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["categories"]; | ||||
|     release_date?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"]; | ||||
|     metadata?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"]; | ||||
|   }; | ||||
|   currencies?: GetCurrenciesQuery["currencies"]["data"]; | ||||
|   item: | ||||
|     | Exclude< | ||||
|         Exclude< | ||||
|           Exclude< | ||||
|             GetLibraryItemQuery["libraryItems"], | ||||
|             null | undefined | ||||
|           >["data"][number]["attributes"], | ||||
|           null | undefined | ||||
|         >["subitems"], | ||||
|         null | undefined | ||||
|       >["data"][number]["attributes"] | ||||
|     | Exclude< | ||||
|         GetLibraryItemsPreviewQuery["libraryItems"], | ||||
|         null | undefined | ||||
|       >["data"][number]["attributes"]; | ||||
|   currencies: AppStaticProps["currencies"]; | ||||
| }; | ||||
| 
 | ||||
| export default function LibraryItemsPreview( | ||||
| @ -30,11 +36,11 @@ export default function LibraryItemsPreview( | ||||
|   const appLayout = useAppLayout(); | ||||
| 
 | ||||
|   return ( | ||||
|     <Link href={`/library/${item.slug}`} passHref> | ||||
|     <Link href={`/library/${item?.slug}`} passHref> | ||||
|       <div | ||||
|         className={`drop-shadow-shade-xl cursor-pointer grid items-end hover:rounded-3xl fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform ${props.className}`} | ||||
|       > | ||||
|         {item.thumbnail.data ? ( | ||||
|         {item?.thumbnail?.data?.attributes ? ( | ||||
|           <Img | ||||
|             image={item.thumbnail.data.attributes} | ||||
|             quality={ImageQuality.Small} | ||||
| @ -44,26 +50,26 @@ export default function LibraryItemsPreview( | ||||
|         )} | ||||
| 
 | ||||
|         <div className="linearbg-obi fine:drop-shadow-shade-lg fine:absolute place-items-start bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2"> | ||||
|           {item.metadata && item.metadata.length > 0 && ( | ||||
|           {item?.metadata && item.metadata.length > 0 && item.metadata[0] && ( | ||||
|             <div className="flex flex-row gap-1"> | ||||
|               <Chip>{prettyItemSubType(item.metadata[0])}</Chip> | ||||
|             </div> | ||||
|           )} | ||||
| 
 | ||||
|           <div> | ||||
|             <h2 className="mobile:text-sm text-lg leading-5">{item.title}</h2> | ||||
|             <h3 className="mobile:text-xs leading-3">{item.subtitle}</h3> | ||||
|             <h2 className="mobile:text-sm text-lg leading-5">{item?.title}</h2> | ||||
|             <h3 className="mobile:text-xs leading-3">{item?.subtitle}</h3> | ||||
|           </div> | ||||
| 
 | ||||
|           <div className="w-full grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:h-0 [scrollbar-width:none] place-content-start"> | ||||
|             {item.categories.data.map((category) => ( | ||||
|             {item?.categories?.data.map((category) => ( | ||||
|               <Chip key={category.id} className="text-sm"> | ||||
|                 {category.attributes.short} | ||||
|                 {category.attributes?.short} | ||||
|               </Chip> | ||||
|             ))} | ||||
|           </div> | ||||
| 
 | ||||
|           {(item.release_date || item.price) && ( | ||||
|           {(item?.release_date || item?.price) && ( | ||||
|             <div className="grid grid-flow-col w-full"> | ||||
|               {item.release_date && ( | ||||
|                 <p className="mobile:text-xs text-sm"> | ||||
| @ -73,7 +79,7 @@ export default function LibraryItemsPreview( | ||||
|                   {prettyDate(item.release_date)} | ||||
|                 </p> | ||||
|               )} | ||||
|               {item.price && props.currencies && ( | ||||
|               {item.price && ( | ||||
|                 <p className="mobile:text-xs text-sm justify-self-end"> | ||||
|                   <span className="material-icons !text-base translate-y-[.15em] mr-1"> | ||||
|                     shopping_cart | ||||
|  | ||||
| @ -69,7 +69,7 @@ export type TOC = { | ||||
| export function getTocFromMarkdawn(text: string, title?: string): TOC { | ||||
|   const toc: TOC = { | ||||
|     title: title ?? "Return to top", | ||||
|     slug: slugify(title) ?? "", | ||||
|     slug: slugify(title), | ||||
|     children: [], | ||||
|   }; | ||||
|   let h2 = -1; | ||||
|  | ||||
| @ -1,26 +1,23 @@ | ||||
| import Chip from "components/Chip"; | ||||
| import Img, { ImageQuality } from "components/Img"; | ||||
| import { GetPostsPreviewQuery } from "graphql/operations-types"; | ||||
| import { GetPostsPreviewQuery } from "graphql/generated"; | ||||
| import Link from "next/link"; | ||||
| import { prettyDate, prettySlug } from "queries/helpers"; | ||||
| 
 | ||||
| export type PostPreviewProps = { | ||||
|   post: { | ||||
|     slug: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["slug"]; | ||||
|     thumbnail: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["thumbnail"]; | ||||
|     translations: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["translations"]; | ||||
|     categories: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["categories"]; | ||||
|     date: GetPostsPreviewQuery["posts"]["data"][number]["attributes"]["date"]; | ||||
|   }; | ||||
|   post: Exclude< | ||||
|     GetPostsPreviewQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| }; | ||||
| 
 | ||||
| export default function PostPreview(props: PostPreviewProps): JSX.Element { | ||||
|   const { post } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <Link href={`/news/${post.slug}`} passHref> | ||||
|     <Link href={`/news/${post?.slug}`} passHref> | ||||
|       <div className="drop-shadow-shade-xl cursor-pointer grid items-end hover:scale-[1.02] transition-transform"> | ||||
|         {post.thumbnail.data ? ( | ||||
|         {post?.thumbnail?.data?.attributes ? ( | ||||
|           <Img | ||||
|             className="rounded-md rounded-b-none" | ||||
|             image={post.thumbnail.data.attributes} | ||||
| @ -30,30 +27,31 @@ export default function PostPreview(props: PostPreviewProps): JSX.Element { | ||||
|           <div className="w-full aspect-[3/2] bg-light rounded-lg"></div> | ||||
|         )} | ||||
|         <div className="linearbg-obi fine:drop-shadow-shade-lg rounded-b-md top-full transition-opacity z-20 grid p-4 gap-2"> | ||||
|           <div className="grid grid-flow-col w-full"> | ||||
|             {post.date && ( | ||||
|           {post?.date && ( | ||||
|             <div className="grid grid-flow-col w-full"> | ||||
|               <p className="mobile:text-xs text-sm"> | ||||
|                 <span className="material-icons !text-base translate-y-[.15em] mr-1"> | ||||
|                   event | ||||
|                 </span> | ||||
|                 {prettyDate(post.date)} | ||||
|               </p> | ||||
|             )} | ||||
|           </div> | ||||
|             </div> | ||||
|           )} | ||||
| 
 | ||||
|           <div> | ||||
|             {post.translations.length > 0 ? ( | ||||
|             {post?.translations?.[0] ? ( | ||||
|               <> | ||||
|                 <h1 className="text-xl">{post.translations[0].title}</h1> | ||||
|                 <p>{post.translations[0].excerpt}</p> | ||||
|               </> | ||||
|             ) : ( | ||||
|               <h1 className="text-lg">{prettySlug(post.slug)}</h1> | ||||
|               <h1 className="text-lg">{prettySlug(post?.slug)}</h1> | ||||
|             )} | ||||
|           </div> | ||||
|           <div className="grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:w-0 [scrollbar-width:none] place-content-start"> | ||||
|             {post.categories.data.map((category) => ( | ||||
|             {post?.categories?.data.map((category) => ( | ||||
|               <Chip key={category.id} className="text-sm"> | ||||
|                 {category.attributes.short} | ||||
|                 {category.attributes?.short} | ||||
|               </Chip> | ||||
|             ))} | ||||
|           </div> | ||||
|  | ||||
| @ -5,8 +5,8 @@ import { MouseEventHandler } from "react"; | ||||
| type NavOptionProps = { | ||||
|   url: string; | ||||
|   icon?: string; | ||||
|   title: string; | ||||
|   subtitle?: string; | ||||
|   title: string | null | undefined; | ||||
|   subtitle?: string | null | undefined; | ||||
|   border?: boolean; | ||||
|   reduced?: boolean; | ||||
|   onClick?: MouseEventHandler<HTMLDivElement>; | ||||
|  | ||||
| @ -2,8 +2,8 @@ import HorizontalLine from "components/HorizontalLine"; | ||||
| 
 | ||||
| type PanelHeaderProps = { | ||||
|   icon?: string; | ||||
|   title: string; | ||||
|   description?: string; | ||||
|   title: string | null | undefined; | ||||
|   description?: string | null | undefined; | ||||
| }; | ||||
| 
 | ||||
| export default function PanelHeader(props: PanelHeaderProps): JSX.Element { | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| import Button from "components/Button"; | ||||
| import HorizontalLine from "components/HorizontalLine"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| 
 | ||||
| type ReturnButtonProps = { | ||||
|   href: string; | ||||
|   title: string; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   title: string | null | undefined; | ||||
|   langui: AppStaticProps["langui"]; | ||||
|   displayOn: ReturnButtonType; | ||||
|   horizontalLine?: boolean; | ||||
|   className?: string; | ||||
|  | ||||
| @ -3,14 +3,14 @@ import HorizontalLine from "components/HorizontalLine"; | ||||
| import NavOption from "components/PanelComponents/NavOption"; | ||||
| import ToolTip from "components/ToolTip"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; | ||||
| import { useMediaDesktop } from "hooks/useMediaQuery"; | ||||
| import Markdown from "markdown-to-jsx"; | ||||
| import Link from "next/link"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| 
 | ||||
| type MainPanelProps = { | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   langui: AppStaticProps["langui"]; | ||||
| }; | ||||
| 
 | ||||
| export default function MainPanel(props: MainPanelProps): JSX.Element { | ||||
|  | ||||
| @ -1,68 +1,67 @@ | ||||
| import Chip from "components/Chip"; | ||||
| import { | ||||
|   GetContentTextQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { RecorderChipFragment } from "graphql/generated"; | ||||
| import { AppStaticProps } from "queries/getAppStaticProps"; | ||||
| import Button from "./Button"; | ||||
| import Img, { ImageQuality } from "./Img"; | ||||
| import ToolTip from "./ToolTip"; | ||||
| 
 | ||||
| type RecorderChipProps = { | ||||
|   className?: string; | ||||
|   recorder: GetContentTextQuery["contents"]["data"][number]["attributes"]["text_set"][number]["transcribers"]["data"][number]; | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   recorder: RecorderChipFragment; | ||||
|   langui: AppStaticProps["langui"]; | ||||
| }; | ||||
| 
 | ||||
| export default function RecorderChip(props: RecorderChipProps): JSX.Element { | ||||
|   const { recorder, langui } = props; | ||||
| 
 | ||||
|   return ( | ||||
|     <ToolTip | ||||
|       content={ | ||||
|         <div className="text-left p-2 py-5 grid gap-8"> | ||||
|           <div className="grid grid-flow-col gap-6 place-items-center place-content-start"> | ||||
|             {recorder.attributes.avatar.data && ( | ||||
|             {recorder.avatar?.data?.attributes && ( | ||||
|               <Img | ||||
|                 className="w-20 rounded-full border-4 border-mid" | ||||
|                 image={recorder.attributes.avatar.data.attributes} | ||||
|                 image={recorder.avatar?.data.attributes} | ||||
|                 quality={ImageQuality.Small} | ||||
|                 rawImg | ||||
|               /> | ||||
|             )} | ||||
|             <div className="grid gap-2"> | ||||
|               <h3 className=" text-2xl">{recorder.attributes.username}</h3> | ||||
|               {recorder.attributes.languages.data.length > 0 && ( | ||||
|               <h3 className=" text-2xl">{recorder.username}</h3> | ||||
|               {recorder.languages?.data && recorder.languages.data.length > 0 && ( | ||||
|                 <div className="flex flex-row flex-wrap gap-1"> | ||||
|                   <p>{langui.languages}:</p> | ||||
|                   {recorder.attributes.languages.data.map((language) => ( | ||||
|                     <Chip key={language.attributes.code}> | ||||
|                       {language.attributes.code.toUpperCase()} | ||||
|                     </Chip> | ||||
|                   {recorder.languages.data.map((language) => ( | ||||
|                     <> | ||||
|                       {language.attributes && ( | ||||
|                         <Chip key={language.attributes.code}> | ||||
|                           {language.attributes.code.toUpperCase()} | ||||
|                         </Chip> | ||||
|                       )} | ||||
|                     </> | ||||
|                   ))} | ||||
|                 </div> | ||||
|               )} | ||||
|               {recorder.attributes.pronouns && ( | ||||
|               {recorder.pronouns && ( | ||||
|                 <div className="flex flex-row flex-wrap gap-1"> | ||||
|                   <p>{langui.pronouns}:</p> | ||||
|                   <Chip>{recorder.attributes.pronouns}</Chip> | ||||
|                   <Chip>{recorder.pronouns}</Chip> | ||||
|                 </div> | ||||
|               )} | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|           {recorder.attributes.bio.length > 0 && ( | ||||
|             <p>{recorder.attributes.bio[0].bio}</p> | ||||
|           )} | ||||
|           {recorder.bio?.[0] && <p>{recorder.bio[0].bio}</p>} | ||||
| 
 | ||||
|           <Button className="cursor-not-allowed">View profile</Button> | ||||
|         </div> | ||||
|       } | ||||
|       placement="top" | ||||
|     > | ||||
|       <Chip key={recorder.id}> | ||||
|         {recorder.attributes.anonymize | ||||
|           ? `Recorder#${recorder.attributes.anonymous_code}` | ||||
|           : recorder.attributes.username} | ||||
|       <Chip key={recorder.anonymous_code}> | ||||
|         {recorder.anonymize | ||||
|           ? `Recorder#${recorder.anonymous_code}` | ||||
|           : recorder.username} | ||||
|       </Chip> | ||||
|     </ToolTip> | ||||
|   ); | ||||
|  | ||||
							
								
								
									
										5
									
								
								src/graphql/fragments/datePicker.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/graphql/fragments/datePicker.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| fragment datePicker on ComponentBasicsDatepicker { | ||||
|   year | ||||
|   month | ||||
|   day | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/graphql/fragments/pricePicker.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/graphql/fragments/pricePicker.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| fragment pricePicker on ComponentBasicsPrice { | ||||
|   amount | ||||
|   currency { | ||||
|     data { | ||||
|       attributes { | ||||
|         symbol | ||||
|         code | ||||
|         rate_to_usd | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/graphql/fragments/recorderChip.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/graphql/fragments/recorderChip.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| fragment recorderChip on Recorder { | ||||
|   username | ||||
|   anonymize | ||||
|   anonymous_code | ||||
|   pronouns | ||||
|   bio(filters: { language: { code: { eq: $language_code } } }) { | ||||
|     bio | ||||
|   } | ||||
|   languages { | ||||
|     data { | ||||
|       attributes { | ||||
|         code | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   avatar { | ||||
|     data { | ||||
|       attributes { | ||||
|         ...uploadImage | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/graphql/fragments/uploadImage.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/graphql/fragments/uploadImage.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| fragment uploadImage on UploadFile { | ||||
|   name | ||||
|   alternativeText | ||||
|   caption | ||||
|   width | ||||
|   height | ||||
|   url | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,178 +0,0 @@ | ||||
| import { readFileSync } from "fs"; | ||||
| import { | ||||
|   GetChronologyItemsQuery, | ||||
|   GetChronologyItemsQueryVariables, | ||||
|   GetContentQuery, | ||||
|   GetContentQueryVariables, | ||||
|   GetContentsQuery, | ||||
|   GetContentsQueryVariables, | ||||
|   GetContentsSlugsQuery, | ||||
|   GetContentsSlugsQueryVariables, | ||||
|   GetContentTextQuery, | ||||
|   GetContentTextQueryVariables, | ||||
|   GetCurrenciesQuery, | ||||
|   GetCurrenciesQueryVariables, | ||||
|   GetErasQuery, | ||||
|   GetErasQueryVariables, | ||||
|   GetLanguagesQuery, | ||||
|   GetLanguagesQueryVariables, | ||||
|   GetLibraryItemQuery, | ||||
|   GetLibraryItemQueryVariables, | ||||
|   GetLibraryItemScansQuery, | ||||
|   GetLibraryItemScansQueryVariables, | ||||
|   GetLibraryItemsPreviewQuery, | ||||
|   GetLibraryItemsPreviewQueryVariables, | ||||
|   GetLibraryItemsSlugsQuery, | ||||
|   GetLibraryItemsSlugsQueryVariables, | ||||
|   GetPostQuery, | ||||
|   GetPostQueryVariables, | ||||
|   GetPostsPreviewQuery, | ||||
|   GetPostsPreviewQueryVariables, | ||||
|   GetPostsSlugsQuery, | ||||
|   GetPostsSlugsQueryVariables, | ||||
|   GetWebsiteInterfaceQuery, | ||||
|   GetWebsiteInterfaceQueryVariables, | ||||
| } from "graphql/operations-types"; | ||||
| 
 | ||||
| async function graphQL(query: string, variables?: string) { | ||||
|   const res = await fetch(`${process.env.URL_GRAPHQL}`, { | ||||
|     method: "POST", | ||||
|     body: JSON.stringify({ | ||||
|       query: query, | ||||
|       variables: variables, | ||||
|     }), | ||||
|     headers: { | ||||
|       "content-type": "application/json", | ||||
|       Authorization: `Bearer ${process.env.ACCESS_TOKEN}`, | ||||
|     }, | ||||
|   }); | ||||
|   return (await res.json()).data; | ||||
| } | ||||
| 
 | ||||
| function getQueryFromOperations(queryName: string): string { | ||||
|   const operations = readFileSync("./src/graphql/operation.graphql", "utf8"); | ||||
|   let startingIndex = -1; | ||||
|   let endingIndex = -1; | ||||
|   const lines = operations.split("\n"); | ||||
|   lines.map((line, index) => { | ||||
|     if (startingIndex === -1) { | ||||
|       if (line.startsWith(`query ${queryName}(`)) startingIndex = index; | ||||
|       if (line.startsWith(`query ${queryName} {`)) startingIndex = index; | ||||
|     } else if (endingIndex === -1) { | ||||
|       if (line.startsWith("query")) endingIndex = index; | ||||
|     } | ||||
|   }); | ||||
|   return lines.slice(startingIndex, endingIndex).join("\n"); | ||||
| } | ||||
| 
 | ||||
| export async function getWebsiteInterface( | ||||
|   variables: GetWebsiteInterfaceQueryVariables | ||||
| ): Promise<GetWebsiteInterfaceQuery> { | ||||
|   const query = getQueryFromOperations("getWebsiteInterface"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getEras( | ||||
|   variables: GetErasQueryVariables | ||||
| ): Promise<GetErasQuery> { | ||||
|   const query = getQueryFromOperations("getEras"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getChronologyItems( | ||||
|   variables: GetChronologyItemsQueryVariables | ||||
| ): Promise<GetChronologyItemsQuery> { | ||||
|   const query = getQueryFromOperations("getChronologyItems"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getLibraryItemsPreview( | ||||
|   variables: GetLibraryItemsPreviewQueryVariables | ||||
| ): Promise<GetLibraryItemsPreviewQuery> { | ||||
|   const query = getQueryFromOperations("getLibraryItemsPreview"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getLibraryItemsSlugs( | ||||
|   variables: GetLibraryItemsSlugsQueryVariables | ||||
| ): Promise<GetLibraryItemsSlugsQuery> { | ||||
|   const query = getQueryFromOperations("getLibraryItemsSlugs"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getLibraryItem( | ||||
|   variables: GetLibraryItemQueryVariables | ||||
| ): Promise<GetLibraryItemQuery> { | ||||
|   const query = getQueryFromOperations("getLibraryItem"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getContentsSlugs( | ||||
|   variables: GetContentsSlugsQueryVariables | ||||
| ): Promise<GetContentsSlugsQuery> { | ||||
|   const query = getQueryFromOperations("getContentsSlugs"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getContents( | ||||
|   variables: GetContentsQueryVariables | ||||
| ): Promise<GetContentsQuery> { | ||||
|   const query = getQueryFromOperations("getContents"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getContent( | ||||
|   variables: GetContentQueryVariables | ||||
| ): Promise<GetContentQuery> { | ||||
|   const query = getQueryFromOperations("getContent"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getContentText( | ||||
|   variables: GetContentTextQueryVariables | ||||
| ): Promise<GetContentTextQuery> { | ||||
|   const query = getQueryFromOperations("getContentText"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getCurrencies( | ||||
|   variables: GetCurrenciesQueryVariables | ||||
| ): Promise<GetCurrenciesQuery> { | ||||
|   const query = getQueryFromOperations("getCurrencies"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getLanguages( | ||||
|   variables: GetLanguagesQueryVariables | ||||
| ): Promise<GetLanguagesQuery> { | ||||
|   const query = getQueryFromOperations("getLanguages"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getPost( | ||||
|   variables: GetPostQueryVariables | ||||
| ): Promise<GetPostQuery> { | ||||
|   const query = getQueryFromOperations("getPost"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getPostsSlugs( | ||||
|   variables: GetPostsSlugsQueryVariables | ||||
| ): Promise<GetPostsSlugsQuery> { | ||||
|   const query = getQueryFromOperations("getPostsSlugs"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getPostsPreview( | ||||
|   variables: GetPostsPreviewQueryVariables | ||||
| ): Promise<GetPostsPreviewQuery> { | ||||
|   const query = getQueryFromOperations("getPostsPreview"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
| 
 | ||||
| export async function getLibraryItemScans( | ||||
|   variables: GetLibraryItemScansQueryVariables | ||||
| ): Promise<GetLibraryItemScansQuery> { | ||||
|   const query = getQueryFromOperations("getLibraryItemScans"); | ||||
|   return await graphQL(query, JSON.stringify(variables)); | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/graphql/operations/getChronologyItems.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/graphql/operations/getChronologyItems.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| query getChronologyItems($language_code: String) { | ||||
|   chronologyItems( | ||||
|     pagination: { limit: -1 } | ||||
|     sort: ["year:asc", "month:asc", "day:asc"] | ||||
|   ) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         year | ||||
|         month | ||||
|         day | ||||
|         displayed_date | ||||
|         events { | ||||
|           id | ||||
|           source { | ||||
|             data { | ||||
|               attributes { | ||||
|                 name | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           translations( | ||||
|             filters: { language: { code: { eq: $language_code } } } | ||||
|           ) { | ||||
|             title | ||||
|             description | ||||
|             note | ||||
|             status | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/graphql/operations/getContent.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/graphql/operations/getContent.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| query getContent($slug: String, $language_code: String) { | ||||
|   contents(filters: { slug: { eq: $slug } }) { | ||||
|     data { | ||||
|       attributes { | ||||
|         slug | ||||
|         titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           pre_title | ||||
|           title | ||||
|           subtitle | ||||
|           description | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               name | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         type { | ||||
|           data { | ||||
|             attributes { | ||||
|               slug | ||||
|               titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|                 title | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         ranged_contents { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               slug | ||||
|               scan_set { | ||||
|                 id | ||||
|               } | ||||
|               library_item { | ||||
|                 data { | ||||
|                   attributes { | ||||
|                     slug | ||||
|                     title | ||||
|                     subtitle | ||||
|                     thumbnail { | ||||
|                       data { | ||||
|                         attributes { | ||||
|                           ...uploadImage | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         text_set { | ||||
|           id | ||||
|         } | ||||
|         video_set { | ||||
|           id | ||||
|         } | ||||
|         audio_set { | ||||
|           id | ||||
|         } | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										114
									
								
								src/graphql/operations/getContentText.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/graphql/operations/getContentText.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| query getContentText($slug: String, $language_code: String) { | ||||
|   contents(filters: { slug: { eq: $slug } }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           pre_title | ||||
|           title | ||||
|           subtitle | ||||
|           description | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               name | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         type { | ||||
|           data { | ||||
|             attributes { | ||||
|               slug | ||||
|               titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|                 title | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         ranged_contents { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               slug | ||||
|               scan_set { | ||||
|                 id | ||||
|               } | ||||
|               library_item { | ||||
|                 data { | ||||
|                   attributes { | ||||
|                     slug | ||||
|                     title | ||||
|                     subtitle | ||||
|                     thumbnail { | ||||
|                       data { | ||||
|                         attributes { | ||||
|                           ...uploadImage | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         text_set_languages: text_set { | ||||
|           language { | ||||
|             data { | ||||
|               attributes { | ||||
|                 code | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         text_set(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           status | ||||
|           text | ||||
|           source_language { | ||||
|             data { | ||||
|               attributes { | ||||
|                 code | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           transcribers { | ||||
|             data { | ||||
|               id | ||||
|               attributes { | ||||
|                 ...recorderChip | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           translators { | ||||
|             data { | ||||
|               id | ||||
|               attributes { | ||||
|                 ...recorderChip | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           proofreaders { | ||||
|             data { | ||||
|               id | ||||
|               attributes { | ||||
|                 ...recorderChip | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           notes | ||||
|         } | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/graphql/operations/getContents.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/graphql/operations/getContents.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| query getContents($language_code: String) { | ||||
|   contents(pagination: { limit: -1 }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           pre_title | ||||
|           title | ||||
|           subtitle | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               name | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         type { | ||||
|           data { | ||||
|             attributes { | ||||
|               slug | ||||
|               titles(filters: { language: { code: { eq: $language_code } } }) { | ||||
|                 title | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         ranged_contents { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               slug | ||||
|               scan_set { | ||||
|                 id | ||||
|               } | ||||
|               library_item { | ||||
|                 data { | ||||
|                   attributes { | ||||
|                     slug | ||||
|                     title | ||||
|                     subtitle | ||||
|                     thumbnail { | ||||
|                       data { | ||||
|                         attributes { | ||||
|                           ...uploadImage | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         text_set { | ||||
|           id | ||||
|         } | ||||
|         video_set { | ||||
|           id | ||||
|         } | ||||
|         audio_set { | ||||
|           id | ||||
|         } | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/graphql/operations/getContentsSlugs.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/graphql/operations/getContentsSlugs.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| query getContentsSlugs { | ||||
|   contents(pagination: { limit: -1 }) { | ||||
|     data { | ||||
|       attributes { | ||||
|         slug | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/graphql/operations/getCurrencies.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/graphql/operations/getCurrencies.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| query getCurrencies { | ||||
|   currencies { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         code | ||||
|         symbol | ||||
|         rate_to_usd | ||||
|         display_decimals | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/graphql/operations/getEras.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/graphql/operations/getEras.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| query getEras($language_code: String) { | ||||
|   chronologyEras(sort: "starting_year") { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         starting_year | ||||
|         ending_year | ||||
|         title(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           title | ||||
|           description | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/graphql/operations/getLanguages.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/graphql/operations/getLanguages.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| query getLanguages { | ||||
|   languages { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         name | ||||
|         code | ||||
|         localized_name | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										387
									
								
								src/graphql/operations/getLibraryItem.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								src/graphql/operations/getLibraryItem.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,387 @@ | ||||
| query getLibraryItem($slug: String, $language_code: String) { | ||||
|   libraryItems(filters: { slug: { eq: $slug } }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         title | ||||
|         subtitle | ||||
|         slug | ||||
|         root_item | ||||
|         primary | ||||
|         digital | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         gallery { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         release_date { | ||||
|           ...datePicker | ||||
|         } | ||||
|         price { | ||||
|           ...pricePicker | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               name | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         size { | ||||
|           width | ||||
|           height | ||||
|           thickness | ||||
|         } | ||||
|         descriptions(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           description | ||||
|         } | ||||
|         metadata { | ||||
|           __typename | ||||
|           ... on ComponentMetadataBooks { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             binding_type | ||||
|             page_count | ||||
|             page_order | ||||
|             languages { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   code | ||||
|                   name | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataVideo { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataGame { | ||||
|             platforms { | ||||
|               data { | ||||
|                 id | ||||
|                 attributes { | ||||
|                   short | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             audio_languages { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   code | ||||
|                   name | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             sub_languages { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   code | ||||
|                   name | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             interface_languages { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   code | ||||
|                   name | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataAudio { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataGroup { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             subitems_type { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         subitem_of { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               title | ||||
|               subtitle | ||||
|               slug | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         subitems { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               title | ||||
|               subtitle | ||||
|               slug | ||||
|               thumbnail { | ||||
|                 data { | ||||
|                   attributes { | ||||
|                     ...uploadImage | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|               release_date { | ||||
|                 ...datePicker | ||||
|               } | ||||
|               price { | ||||
|                 ...pricePicker | ||||
|               } | ||||
|               categories { | ||||
|                 data { | ||||
|                   id | ||||
|                   attributes { | ||||
|                     name | ||||
|                     short | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|               metadata { | ||||
|                 __typename | ||||
|                 ... on ComponentMetadataBooks { | ||||
|                   subtype { | ||||
|                     data { | ||||
|                       attributes { | ||||
|                         slug | ||||
|                         titles( | ||||
|                           filters: { | ||||
|                             language: { code: { eq: $language_code } } | ||||
|                           } | ||||
|                         ) { | ||||
|                           title | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 ... on ComponentMetadataGame { | ||||
|                   platforms { | ||||
|                     data { | ||||
|                       id | ||||
|                       attributes { | ||||
|                         short | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 ... on ComponentMetadataVideo { | ||||
|                   subtype { | ||||
|                     data { | ||||
|                       attributes { | ||||
|                         slug | ||||
|                         titles( | ||||
|                           filters: { | ||||
|                             language: { code: { eq: $language_code } } | ||||
|                           } | ||||
|                         ) { | ||||
|                           title | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 ... on ComponentMetadataAudio { | ||||
|                   subtype { | ||||
|                     data { | ||||
|                       attributes { | ||||
|                         slug | ||||
|                         titles( | ||||
|                           filters: { | ||||
|                             language: { code: { eq: $language_code } } | ||||
|                           } | ||||
|                         ) { | ||||
|                           title | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 ... on ComponentMetadataGroup { | ||||
|                   subtype { | ||||
|                     data { | ||||
|                       attributes { | ||||
|                         slug | ||||
|                         titles( | ||||
|                           filters: { | ||||
|                             language: { code: { eq: $language_code } } | ||||
|                           } | ||||
|                         ) { | ||||
|                           title | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                   subitems_type { | ||||
|                     data { | ||||
|                       attributes { | ||||
|                         slug | ||||
|                         titles( | ||||
|                           filters: { | ||||
|                             language: { code: { eq: $language_code } } | ||||
|                           } | ||||
|                         ) { | ||||
|                           title | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         submerchs { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               slug | ||||
|               title | ||||
|               subtitle | ||||
|               thumbnail { | ||||
|                 data { | ||||
|                   attributes { | ||||
|                     ...uploadImage | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         contents(pagination: { limit: -1 }) { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               slug | ||||
|               range { | ||||
|                 __typename | ||||
|                 ... on ComponentRangePageRange { | ||||
|                   starting_page | ||||
|                   ending_page | ||||
|                 } | ||||
|                 ... on ComponentRangeTimeRange { | ||||
|                   starting_time | ||||
|                   ending_time | ||||
|                 } | ||||
|               } | ||||
|               scan_set { | ||||
|                 id | ||||
|               } | ||||
|               content { | ||||
|                 data { | ||||
|                   attributes { | ||||
|                     slug | ||||
|                     categories { | ||||
|                       data { | ||||
|                         id | ||||
|                         attributes { | ||||
|                           name | ||||
|                           short | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                     type { | ||||
|                       data { | ||||
|                         attributes { | ||||
|                           slug | ||||
|                           titles( | ||||
|                             filters: { | ||||
|                               language: { code: { eq: $language_code } } | ||||
|                             } | ||||
|                           ) { | ||||
|                             title | ||||
|                           } | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                     titles( | ||||
|                       filters: { language: { code: { eq: $language_code } } } | ||||
|                     ) { | ||||
|                       pre_title | ||||
|                       title | ||||
|                       subtitle | ||||
|                     } | ||||
|                     text_set { | ||||
|                       id | ||||
|                     } | ||||
|                     video_set { | ||||
|                       id | ||||
|                     } | ||||
|                     audio_set { | ||||
|                       id | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										97
									
								
								src/graphql/operations/getLibraryItemScans.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/graphql/operations/getLibraryItemScans.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| query getLibraryItemScans($slug: String, $language_code: String) { | ||||
|   libraryItems(filters: { slug: { eq: $slug } }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         title | ||||
|         subtitle | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         contents(pagination: { limit: -1 }) { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               slug | ||||
|               range { | ||||
|                 __typename | ||||
|                 ... on ComponentRangePageRange { | ||||
|                   starting_page | ||||
|                   ending_page | ||||
|                 } | ||||
|                 ... on ComponentRangeTimeRange { | ||||
|                   starting_time | ||||
|                   ending_time | ||||
|                 } | ||||
|               } | ||||
|               scan_set_languages: scan_set { | ||||
|                 language { | ||||
|                   data { | ||||
|                     attributes { | ||||
|                       code | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|               scan_set( | ||||
|                 filters: { | ||||
|                   or: [ | ||||
|                     { language: { code: { eq: "xx" } } } | ||||
|                     { language: { code: { eq: $language_code } } } | ||||
|                   ] | ||||
|                 } | ||||
|               ) { | ||||
|                 status | ||||
|                 source_language { | ||||
|                   data { | ||||
|                     attributes { | ||||
|                       code | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 scanners { | ||||
|                   data { | ||||
|                     id | ||||
|                     attributes { | ||||
|                       ...recorderChip | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 cleaners { | ||||
|                   data { | ||||
|                     id | ||||
|                     attributes { | ||||
|                       ...recorderChip | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 typesetters { | ||||
|                   data { | ||||
|                     id | ||||
|                     attributes { | ||||
|                       ...recorderChip | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|                 notes | ||||
|                 pages(pagination: { limit: -1 }) { | ||||
|                   data { | ||||
|                     id | ||||
|                     attributes { | ||||
|                       ...uploadImage | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										117
									
								
								src/graphql/operations/getLibraryItemsPreview.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/graphql/operations/getLibraryItemsPreview.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | ||||
| query getLibraryItemsPreview($language_code: String) { | ||||
|   libraryItems(pagination: { limit: -1 }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         title | ||||
|         subtitle | ||||
|         slug | ||||
|         root_item | ||||
|         primary | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         release_date { | ||||
|           ...datePicker | ||||
|         } | ||||
|         price { | ||||
|           ...pricePicker | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               name | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         metadata { | ||||
|           __typename | ||||
|           ... on ComponentMetadataBooks { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataGame { | ||||
|             platforms { | ||||
|               data { | ||||
|                 id | ||||
|                 attributes { | ||||
|                   short | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataVideo { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataAudio { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           ... on ComponentMetadataGroup { | ||||
|             subtype { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             subitems_type { | ||||
|               data { | ||||
|                 attributes { | ||||
|                   slug | ||||
|                   titles( | ||||
|                     filters: { language: { code: { eq: $language_code } } } | ||||
|                   ) { | ||||
|                     title | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/graphql/operations/getLibraryItemsSlugs.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/graphql/operations/getLibraryItemsSlugs.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| query getLibraryItemsSlugs { | ||||
|   libraryItems(pagination: { limit: -1 }) { | ||||
|     data { | ||||
|       attributes { | ||||
|         slug | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/graphql/operations/getPost.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/graphql/operations/getPost.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| query getPost($slug: String, $language_code: String) { | ||||
|   posts(filters: { slug: { eq: $slug } }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         updatedAt | ||||
|         date { | ||||
|           ...datePicker | ||||
|         } | ||||
|         authors { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               ...recorderChip | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               name | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         hidden | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         translations_languages: translations { | ||||
|           language { | ||||
|             data { | ||||
|               attributes { | ||||
|                 code | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         translations(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           status | ||||
|           title | ||||
|           excerpt | ||||
|           thumbnail { | ||||
|             data { | ||||
|               attributes { | ||||
|                 ...uploadImage | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           body | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										39
									
								
								src/graphql/operations/getPostsPreview.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/graphql/operations/getPostsPreview.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| query getPostsPreview($language_code: String) { | ||||
|   posts(filters: { hidden: { eq: false } }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|         date { | ||||
|           ...datePicker | ||||
|         } | ||||
|         categories { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               short | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         thumbnail { | ||||
|           data { | ||||
|             attributes { | ||||
|               ...uploadImage | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         translations(filters: { language: { code: { eq: $language_code } } }) { | ||||
|           title | ||||
|           excerpt | ||||
|           thumbnail { | ||||
|             data { | ||||
|               attributes { | ||||
|                 ...uploadImage | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										10
									
								
								src/graphql/operations/getPostsSlugs.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/graphql/operations/getPostsSlugs.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| query getPostsSlugs { | ||||
|   posts(filters: { hidden: { eq: false } }) { | ||||
|     data { | ||||
|       id | ||||
|       attributes { | ||||
|         slug | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										138
									
								
								src/graphql/operations/getWebsiteInterface.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/graphql/operations/getWebsiteInterface.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | ||||
| query getWebsiteInterface($language_code: String) { | ||||
|   websiteInterfaces( | ||||
|     filters: { ui_language: { code: { eq: $language_code } } } | ||||
|   ) { | ||||
|     data { | ||||
|       attributes { | ||||
|         library | ||||
|         contents | ||||
|         wiki | ||||
|         chronicles | ||||
|         library_short_description | ||||
|         contents_short_description | ||||
|         wiki_short_description | ||||
|         chronicles_short_description | ||||
|         news | ||||
|         merch | ||||
|         gallery | ||||
|         archives | ||||
|         about_us | ||||
|         licensing_notice | ||||
|         copyright_notice | ||||
|         contents_description | ||||
|         type | ||||
|         category | ||||
|         categories | ||||
|         size | ||||
|         release_date | ||||
|         release_year | ||||
|         details | ||||
|         price | ||||
|         width | ||||
|         height | ||||
|         thickness | ||||
|         subitem | ||||
|         subitems | ||||
|         subitem_of | ||||
|         variant | ||||
|         variants | ||||
|         variant_of | ||||
|         summary | ||||
|         audio | ||||
|         video | ||||
|         textual | ||||
|         game | ||||
|         other | ||||
|         return_to | ||||
|         left_to_right | ||||
|         right_to_left | ||||
|         page | ||||
|         pages | ||||
|         page_order | ||||
|         binding | ||||
|         type_information | ||||
|         front_matter | ||||
|         back_matter | ||||
|         open_content | ||||
|         read_content | ||||
|         watch_content | ||||
|         listen_content | ||||
|         view_scans | ||||
|         paperback | ||||
|         hardcover | ||||
|         languages | ||||
|         select_language | ||||
|         language | ||||
|         library_description | ||||
|         wiki_description | ||||
|         chronicles_description | ||||
|         news_description | ||||
|         merch_description | ||||
|         gallery_description | ||||
|         archives_description | ||||
|         about_us_description | ||||
|         page_not_found | ||||
|         default_description | ||||
|         name | ||||
|         show_subitems | ||||
|         show_primary_items | ||||
|         show_secondary_items | ||||
|         no_type | ||||
|         no_year | ||||
|         order_by | ||||
|         group_by | ||||
|         select_option_sidebar | ||||
|         group | ||||
|         settings | ||||
|         theme | ||||
|         light | ||||
|         auto | ||||
|         dark | ||||
|         font_size | ||||
|         player_name | ||||
|         currency | ||||
|         font | ||||
|         calculated | ||||
|         status_incomplete | ||||
|         status_draft | ||||
|         status_review | ||||
|         status_done | ||||
|         incomplete | ||||
|         draft | ||||
|         review | ||||
|         done | ||||
|         status | ||||
|         transcribers | ||||
|         translators | ||||
|         proofreaders | ||||
|         transcript_notice | ||||
|         translation_notice | ||||
|         source_language | ||||
|         pronouns | ||||
|         no_category | ||||
|         item | ||||
|         items | ||||
|         content | ||||
|         result | ||||
|         results | ||||
|         language_switch_message | ||||
|         open_settings | ||||
|         change_language | ||||
|         open_search | ||||
|         chronology | ||||
|         accords_handbook | ||||
|         legality | ||||
|         members | ||||
|         sharing_policy | ||||
|         contact_us | ||||
|         email | ||||
|         email_gdpr_notice | ||||
|         message | ||||
|         send | ||||
|         response_invalid_code | ||||
|         response_invalid_email | ||||
|         response_email_success | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										9
									
								
								src/graphql/sdk.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/graphql/sdk.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| import { GraphQLClient } from "graphql-request"; | ||||
| import { getSdk, UploadFile } from "graphql/generated"; | ||||
| 
 | ||||
| export function getReadySdk() { | ||||
|   const client = new GraphQLClient(process.env.URL_GRAPHQL ?? "", { | ||||
|     headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` }, | ||||
|   }); | ||||
|   return getSdk(client); | ||||
| } | ||||
| @ -7,23 +7,27 @@ import ReturnButton, { | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { getPost } from "graphql/operations"; | ||||
| import { GetPostQuery } from "graphql/operations-types"; | ||||
| 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 AccordsHandbookProps extends AppStaticProps { | ||||
|   post: GetPostQuery["posts"]["data"][number]["attributes"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| } | ||||
| 
 | ||||
| export default function AccordsHandbook( | ||||
|   props: AccordsHandbookProps | ||||
| ): JSX.Element { | ||||
| export default function AccordsHandbook(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const router = useRouter(); | ||||
|   const locales = getLocalesFromLanguages(post.translations_languages); | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
| @ -34,12 +38,7 @@ export default function AccordsHandbook( | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       {post.translations.length > 0 && post.translations[0].body && ( | ||||
|         <TOC | ||||
|           text={post.translations[0].body} | ||||
|           title={post.translations[0].title} | ||||
|         /> | ||||
|       )} | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
| @ -53,7 +52,7 @@ export default function AccordsHandbook( | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={post.translations[0].body} /> | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
| @ -66,11 +65,7 @@ export default function AccordsHandbook( | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={ | ||||
|         post.translations.length > 0 | ||||
|           ? post.translations[0].title | ||||
|           : prettySlug(post.slug) | ||||
|       } | ||||
|       navTitle={title} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
| @ -80,16 +75,17 @@ export default function AccordsHandbook( | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: AccordsHandbookProps }> { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = "accords-handbook"; | ||||
|   const props: AccordsHandbookProps = { | ||||
|   const post = await sdk.getPost({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!post.posts) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     post: ( | ||||
|       await getPost({ | ||||
|         slug: slug, | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).posts.data[0].attributes, | ||||
|     post: post.posts.data[0].attributes, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -8,31 +8,41 @@ import ReturnButton, { | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { getPost } from "graphql/operations"; | ||||
| import { GetPostQuery } from "graphql/operations-types"; | ||||
| 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, randomInt } from "queries/helpers"; | ||||
| import { | ||||
|   getLocalesFromLanguages, | ||||
|   prettySlug, | ||||
|   randomInt, | ||||
| } from "queries/helpers"; | ||||
| import { useState } from "react"; | ||||
| 
 | ||||
| interface ContactProps extends AppStaticProps { | ||||
|   post: GetPostQuery["posts"]["data"][number]["attributes"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| } | ||||
| 
 | ||||
| export default function AboutUs(props: ContactProps): JSX.Element { | ||||
| export default function AboutUs(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const router = useRouter(); | ||||
|   const [formResponse, setFormResponse] = useState(""); | ||||
|   const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">( | ||||
|     "stale" | ||||
|   ); | ||||
|   const locales = getLocalesFromLanguages(post.translations_languages); | ||||
|   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 subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
| @ -42,12 +52,7 @@ export default function AboutUs(props: ContactProps): JSX.Element { | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       {post.translations.length > 0 && post.translations[0].body && ( | ||||
|         <TOC | ||||
|           text={post.translations[0].body} | ||||
|           title={post.translations[0].title} | ||||
|         /> | ||||
|       )} | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
| @ -61,7 +66,7 @@ export default function AboutUs(props: ContactProps): JSX.Element { | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={post.translations[0].body} /> | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
| @ -110,13 +115,13 @@ export default function AboutUs(props: ContactProps): JSX.Element { | ||||
|                 .then((response: ResponseMailProps) => { | ||||
|                   switch (response.code) { | ||||
|                     case "OKAY": | ||||
|                       setFormResponse(langui.response_email_success); | ||||
|                       setFormResponse(langui.response_email_success ?? ""); | ||||
|                       setFormState("completed"); | ||||
| 
 | ||||
|                       break; | ||||
| 
 | ||||
|                     case "EENVELOPE": | ||||
|                       setFormResponse(langui.response_invalid_email); | ||||
|                       setFormResponse(langui.response_invalid_email ?? ""); | ||||
|                       setFormState("stale"); | ||||
|                       break; | ||||
| 
 | ||||
| @ -127,7 +132,7 @@ export default function AboutUs(props: ContactProps): JSX.Element { | ||||
|                   } | ||||
|                 }); | ||||
|             } else { | ||||
|               setFormResponse(langui.response_invalid_code); | ||||
|               setFormResponse(langui.response_invalid_code ?? ""); | ||||
|               setFormState("stale"); | ||||
|               setRandomNumber1(randomInt(0, 10)); | ||||
|               setRandomNumber2(randomInt(0, 10)); | ||||
| @ -194,7 +199,7 @@ export default function AboutUs(props: ContactProps): JSX.Element { | ||||
| 
 | ||||
|             <input | ||||
|               type="submit" | ||||
|               value={langui.send} | ||||
|               value={langui.send ?? "Send"} | ||||
|               className="w-min !px-6" | ||||
|               disabled={formState !== "stale"} | ||||
|             /> | ||||
| @ -224,16 +229,17 @@ export default function AboutUs(props: ContactProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: ContactProps }> { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = "contact"; | ||||
|   const props: ContactProps = { | ||||
|   const post = await sdk.getPost({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!post.posts) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     post: ( | ||||
|       await getPost({ | ||||
|         slug: slug, | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).posts.data[0].attributes, | ||||
|     post: post.posts.data[0].attributes, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -5,9 +5,9 @@ import SubPanel from "components/Panels/SubPanel"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| 
 | ||||
| interface AboutUsProps extends AppStaticProps {} | ||||
| interface Props extends AppStaticProps {} | ||||
| 
 | ||||
| export default function AboutUs(props: AboutUsProps): JSX.Element { | ||||
| export default function AboutUs(props: Props): JSX.Element { | ||||
|   const { langui } = props; | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
| @ -38,8 +38,8 @@ export default function AboutUs(props: AboutUsProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: AboutUsProps }> { | ||||
|   const props: AboutUsProps = { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|   }; | ||||
|   return { | ||||
|  | ||||
| @ -7,21 +7,27 @@ import ReturnButton, { | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { getPost } from "graphql/operations"; | ||||
| import { GetPostQuery } from "graphql/operations-types"; | ||||
| 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 SiteInfoProps extends AppStaticProps { | ||||
|   post: GetPostQuery["posts"]["data"][number]["attributes"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| } | ||||
| 
 | ||||
| export default function SiteInformation(props: SiteInfoProps): JSX.Element { | ||||
| export default function SiteInformation(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const router = useRouter(); | ||||
|   const locales = getLocalesFromLanguages(post.translations_languages); | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
| 
 | ||||
|   const body = post?.translations?.[0]?.body ?? ""; | ||||
|   const title = post?.translations?.[0]?.title ?? prettySlug(post?.slug); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
| @ -32,12 +38,7 @@ export default function SiteInformation(props: SiteInfoProps): JSX.Element { | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       {post.translations.length > 0 && post.translations[0].body && ( | ||||
|         <TOC | ||||
|           text={post.translations[0].body} | ||||
|           title={post.translations[0].title} | ||||
|         /> | ||||
|       )} | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
| @ -51,7 +52,7 @@ export default function SiteInformation(props: SiteInfoProps): JSX.Element { | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={post.translations[0].body} /> | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
| @ -64,11 +65,7 @@ export default function SiteInformation(props: SiteInfoProps): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={ | ||||
|         post.translations.length > 0 | ||||
|           ? post.translations[0].title | ||||
|           : prettySlug(post.slug) | ||||
|       } | ||||
|       navTitle={title} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
| @ -78,16 +75,17 @@ export default function SiteInformation(props: SiteInfoProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: SiteInfoProps }> { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = "legality"; | ||||
|   const props: SiteInfoProps = { | ||||
|   const post = await sdk.getPost({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!post.posts) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     post: ( | ||||
|       await getPost({ | ||||
|         slug: slug, | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).posts.data[0].attributes, | ||||
|     post: post.posts.data[0].attributes, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -7,22 +7,27 @@ import ReturnButton, { | ||||
| } from "components/PanelComponents/ReturnButton"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { getPost } from "graphql/operations"; | ||||
| import { GetPostQuery } from "graphql/operations-types"; | ||||
| 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 SharingPolicyProps extends AppStaticProps { | ||||
|   post: GetPostQuery["posts"]["data"][number]["attributes"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| } | ||||
| 
 | ||||
| export default function SharingPolicy(props: SharingPolicyProps): JSX.Element { | ||||
| export default function SharingPolicy(props: Props): JSX.Element { | ||||
|   const { langui, post } = props; | ||||
|   const locales = getLocalesFromLanguages(post.translations_languages); | ||||
|   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 | ||||
| @ -32,12 +37,7 @@ export default function SharingPolicy(props: SharingPolicyProps): JSX.Element { | ||||
|         title={langui.about_us} | ||||
|         horizontalLine | ||||
|       /> | ||||
|       {post.translations.length > 0 && post.translations[0].body && ( | ||||
|         <TOC | ||||
|           text={post.translations[0].body} | ||||
|           title={post.translations[0].title} | ||||
|         /> | ||||
|       )} | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
| @ -51,7 +51,7 @@ export default function SharingPolicy(props: SharingPolicyProps): JSX.Element { | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={post.translations[0].body} /> | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
| @ -64,11 +64,7 @@ export default function SharingPolicy(props: SharingPolicyProps): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={ | ||||
|         post.translations.length > 0 | ||||
|           ? post.translations[0].title | ||||
|           : prettySlug(post.slug) | ||||
|       } | ||||
|       navTitle={title} | ||||
|       subPanel={subPanel} | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
| @ -78,16 +74,17 @@ export default function SharingPolicy(props: SharingPolicyProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: SharingPolicyProps }> { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = "sharing-policy"; | ||||
|   const props: SharingPolicyProps = { | ||||
|   const post = await sdk.getPost({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!post.posts) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     post: ( | ||||
|       await getPost({ | ||||
|         slug: slug, | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).posts.data[0].attributes, | ||||
|     post: post.posts.data[0].attributes, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -64,7 +64,6 @@ export default async function Mail( | ||||
|   req: NextApiRequest, | ||||
|   res: NextApiResponse<ResponseMailProps> | ||||
| ) { | ||||
|   console.log(req.body); | ||||
|   const body = req.body as RequestProps; | ||||
|   const { serverRuntimeConfig } = getConfig(); | ||||
| 
 | ||||
|  | ||||
| @ -13,8 +13,8 @@ import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import RecorderChip from "components/RecorderChip"; | ||||
| import ToolTip from "components/ToolTip"; | ||||
| import { getContentsSlugs, getContentText } from "graphql/operations"; | ||||
| import { GetContentTextQuery } from "graphql/operations-types"; | ||||
| import { GetContentTextQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { | ||||
|   GetStaticPathsContext, | ||||
|   GetStaticPathsResult, | ||||
| @ -33,15 +33,21 @@ import { | ||||
| } from "queries/helpers"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   content: GetContentTextQuery["contents"]["data"][number]["attributes"]; | ||||
|   contentId: GetContentTextQuery["contents"]["data"][number]["id"]; | ||||
|   content: Exclude< | ||||
|     GetContentTextQuery["contents"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
|   contentId: Exclude< | ||||
|     GetContentTextQuery["contents"], | ||||
|     null | undefined | ||||
|   >["data"][number]["id"]; | ||||
| } | ||||
| 
 | ||||
| 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 locales = getLocalesFromLanguages(content?.text_set_languages); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
| @ -53,7 +59,7 @@ export default function Content(props: Props): JSX.Element { | ||||
|         horizontalLine | ||||
|       /> | ||||
| 
 | ||||
|       {content.text_set.length > 0 && content.text_set[0].source_language.data && ( | ||||
|       {content?.text_set?.[0]?.source_language?.data?.attributes && ( | ||||
|         <div className="grid gap-5"> | ||||
|           <h2 className="text-xl"> | ||||
|             {content.text_set[0].source_language.data.attributes.code === | ||||
| @ -91,134 +97,157 @@ export default function Content(props: Props): JSX.Element { | ||||
|             </ToolTip> | ||||
|           </div> | ||||
| 
 | ||||
|           {content.text_set[0].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) => ( | ||||
|                   <RecorderChip | ||||
|                     key={recorder.id} | ||||
|                     langui={langui} | ||||
|                     recorder={recorder} | ||||
|                   /> | ||||
|                 ))} | ||||
|           {content.text_set[0].transcribers && | ||||
|             content.text_set[0].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) => ( | ||||
|                     <> | ||||
|                       {recorder.attributes && ( | ||||
|                         <RecorderChip | ||||
|                           key={recorder.id} | ||||
|                           langui={langui} | ||||
|                           recorder={recorder.attributes} | ||||
|                         /> | ||||
|                       )} | ||||
|                     </> | ||||
|                   ))} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|             )} | ||||
| 
 | ||||
|           {content.text_set[0].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) => ( | ||||
|                   <RecorderChip | ||||
|                     key={recorder.id} | ||||
|                     langui={langui} | ||||
|                     recorder={recorder} | ||||
|                   /> | ||||
|                 ))} | ||||
|           {content.text_set[0].translators && | ||||
|             content.text_set[0].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) => ( | ||||
|                     <> | ||||
|                       {recorder.attributes && ( | ||||
|                         <RecorderChip | ||||
|                           key={recorder.id} | ||||
|                           langui={langui} | ||||
|                           recorder={recorder.attributes} | ||||
|                         /> | ||||
|                       )} | ||||
|                     </> | ||||
|                   ))} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|             )} | ||||
| 
 | ||||
|           {content.text_set[0].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) => ( | ||||
|                   <RecorderChip | ||||
|                     key={recorder.id} | ||||
|                     langui={langui} | ||||
|                     recorder={recorder} | ||||
|                   /> | ||||
|                 ))} | ||||
|           {content.text_set[0].proofreaders && | ||||
|             content.text_set[0].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) => ( | ||||
|                     <> | ||||
|                       {recorder.attributes && ( | ||||
|                         <RecorderChip | ||||
|                           key={recorder.id} | ||||
|                           langui={langui} | ||||
|                           recorder={recorder.attributes} | ||||
|                         /> | ||||
|                       )} | ||||
|                     </> | ||||
|                   ))} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|             )} | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       {content.text_set.length > 0 && content.text_set[0].text && ( | ||||
|         <> | ||||
|           <HorizontalLine /> | ||||
|           <TOC | ||||
|             text={content.text_set[0].text} | ||||
|             title={ | ||||
|               content.titles.length > 0 | ||||
|                 ? prettyinlineTitle( | ||||
|                     content.titles[0].pre_title, | ||||
|                     content.titles[0].title, | ||||
|                     content.titles[0].subtitle | ||||
|                   ) | ||||
|                 : prettySlug(content.slug) | ||||
|             } | ||||
|           /> | ||||
|         </> | ||||
|       )} | ||||
|       {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) | ||||
|               } | ||||
|             /> | ||||
|           </> | ||||
|         )} | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href={`/contents/${content.slug}`} | ||||
|         href={`/contents/${content?.slug}`} | ||||
|         title={langui.content} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       <div className="grid place-items-center"> | ||||
|         <ThumbnailHeader | ||||
|           thumbnail={content.thumbnail.data?.attributes} | ||||
|           pre_title={ | ||||
|             content.titles.length > 0 ? content.titles[0].pre_title : undefined | ||||
|           } | ||||
|           title={ | ||||
|             content.titles.length > 0 | ||||
|               ? content.titles[0].title | ||||
|               : prettySlug(content.slug) | ||||
|           } | ||||
|           subtitle={ | ||||
|             content.titles.length > 0 ? content.titles[0].subtitle : undefined | ||||
|           } | ||||
|           description={ | ||||
|             content.titles.length > 0 | ||||
|               ? content.titles[0].description | ||||
|               : undefined | ||||
|           } | ||||
|           type={content.type} | ||||
|           categories={content.categories} | ||||
|           langui={langui} | ||||
|         /> | ||||
| 
 | ||||
|         <HorizontalLine /> | ||||
| 
 | ||||
|         {locales.includes(router.locale ?? "en") ? ( | ||||
|           <Markdawn text={content.text_set[0].text} /> | ||||
|         ) : ( | ||||
|           <LanguageSwitcher | ||||
|             locales={locales} | ||||
|             languages={props.languages} | ||||
|             langui={props.langui} | ||||
|       {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 | ||||
|             } | ||||
|             description={ | ||||
|               content.titles && content.titles.length > 0 | ||||
|                 ? content.titles[0]?.description | ||||
|                 : undefined | ||||
|             } | ||||
|             type={content.type} | ||||
|             categories={content.categories} | ||||
|             langui={langui} | ||||
|           /> | ||||
|         )} | ||||
|       </div> | ||||
| 
 | ||||
|           <HorizontalLine /> | ||||
| 
 | ||||
|           {locales.includes(router.locale ?? "en") ? ( | ||||
|             <Markdawn text={content.text_set?.[0]?.text ?? ""} /> | ||||
|           ) : ( | ||||
|             <LanguageSwitcher | ||||
|               locales={locales} | ||||
|               languages={props.languages} | ||||
|               langui={props.langui} | ||||
|             /> | ||||
|           )} | ||||
|         </div> | ||||
|       )} | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   let description = ""; | ||||
|   if (content.type.data) { | ||||
|   if (content?.type?.data) { | ||||
|     description += `${langui.type}: `; | ||||
|     if (content.type.data.attributes.titles.length > 0) { | ||||
|       description += content.type.data.attributes.titles[0].title; | ||||
|     } else { | ||||
|       description += prettySlug(content.type.data.attributes.slug); | ||||
|     } | ||||
| 
 | ||||
|     description += | ||||
|       content.type.data.attributes?.titles?.[0]?.title ?? | ||||
|       prettySlug(content.type.data.attributes?.slug); | ||||
| 
 | ||||
|     description += "\n"; | ||||
|   } | ||||
|   if (content.categories.data.length > 0) { | ||||
|   if (content?.categories?.data && content.categories.data.length > 0) { | ||||
|     description += `${langui.categories}: `; | ||||
|     description += content.categories.data | ||||
|       .map((category) => category.attributes.short) | ||||
|       .map((category) => category.attributes?.short) | ||||
|       .join(" | "); | ||||
|     description += "\n"; | ||||
|   } | ||||
| @ -226,15 +255,15 @@ export default function Content(props: Props): JSX.Element { | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={ | ||||
|         content.titles.length > 0 | ||||
|         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) | ||||
|           : prettySlug(content?.slug) | ||||
|       } | ||||
|       thumbnail={content.thumbnail.data?.attributes} | ||||
|       thumbnail={content?.thumbnail?.data?.attributes ?? undefined} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       description={description} | ||||
| @ -246,18 +275,19 @@ export default function Content(props: Props): JSX.Element { | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = context.params?.slug?.toString() ?? ""; | ||||
|   const content = ( | ||||
|     await getContentText({ | ||||
|       slug: slug, | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).contents.data[0]; | ||||
|   if (!content) return { notFound: true }; | ||||
|   const content = await sdk.getContentText({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
| 
 | ||||
|   if (!content.contents || content.contents.data.length === 0) | ||||
|     return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     content: content.attributes, | ||||
|     contentId: content.id, | ||||
|     content: content.contents.data[0].attributes, | ||||
|     contentId: content.contents.data[0].id, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -267,11 +297,13 @@ export async function getStaticProps( | ||||
| export async function getStaticPaths( | ||||
|   context: GetStaticPathsContext | ||||
| ): Promise<GetStaticPathsResult> { | ||||
|   const contents = await getContentsSlugs({}); | ||||
|   const sdk = getReadySdk(); | ||||
|   const contents = await sdk.getContentsSlugs(); | ||||
|   const paths: GetStaticPathsResult["paths"] = []; | ||||
|   contents.contents.data.map((item) => { | ||||
|   contents.contents?.data.map((item) => { | ||||
|     context.locales?.map((local) => { | ||||
|       paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|       if (item.attributes) | ||||
|         paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|     }); | ||||
|   }); | ||||
|   return { | ||||
| @ -287,12 +319,12 @@ function useTesting(props: Props) { | ||||
|   const contentURL = `/admin/content-manager/collectionType/api::content.content/${contentId}`; | ||||
| 
 | ||||
|   if (router.locale === "en") { | ||||
|     if (content.categories.data.length === 0) { | ||||
|     if (content?.categories?.data.length === 0) { | ||||
|       prettyTestError(router, "Missing categories", ["content"], contentURL); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (content.ranged_contents.data.length === 0) { | ||||
|   if (content?.ranged_contents?.data.length === 0) { | ||||
|     prettyTestWarning( | ||||
|       router, | ||||
|       "Unconnected to any source", | ||||
| @ -301,7 +333,7 @@ function useTesting(props: Props) { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (content.text_set.length === 0) { | ||||
|   if (content?.text_set?.length === 0) { | ||||
|     prettyTestWarning( | ||||
|       router, | ||||
|       "Has no textset, nor audioset, nor videoset", | ||||
| @ -310,7 +342,7 @@ function useTesting(props: Props) { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (content.text_set.length > 1) { | ||||
|   if (content?.text_set && content.text_set.length > 1) { | ||||
|     prettyTestError( | ||||
|       router, | ||||
|       "More than one textset for this language", | ||||
| @ -319,10 +351,10 @@ function useTesting(props: Props) { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (content.text_set.length === 1) { | ||||
|   if (content?.text_set?.length === 1) { | ||||
|     const textset = content.text_set[0]; | ||||
| 
 | ||||
|     if (!textset.text) { | ||||
|     if (!textset?.text) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing text", | ||||
| @ -330,16 +362,18 @@ function useTesting(props: Props) { | ||||
|         contentURL | ||||
|       ); | ||||
|     } | ||||
|     if (!textset.source_language.data) { | ||||
|     if (!textset?.source_language?.data) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing source language", | ||||
|         ["content", "text_set"], | ||||
|         contentURL | ||||
|       ); | ||||
|     } else if (textset.source_language.data.attributes.code === router.locale) { | ||||
|     } else if ( | ||||
|       textset.source_language.data.attributes?.code === router.locale | ||||
|     ) { | ||||
|       // This is a transcript
 | ||||
|       if (textset.transcribers.data.length === 0) { | ||||
|       if (textset.transcribers?.data.length === 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Missing transcribers attribution", | ||||
| @ -347,7 +381,7 @@ function useTesting(props: Props) { | ||||
|           contentURL | ||||
|         ); | ||||
|       } | ||||
|       if (textset.translators.data.length > 0) { | ||||
|       if (textset.translators && textset.translators.data.length > 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Transcripts shouldn't have translators", | ||||
| @ -357,7 +391,7 @@ function useTesting(props: Props) { | ||||
|       } | ||||
|     } else { | ||||
|       // This is a translation
 | ||||
|       if (textset.translators.data.length === 0) { | ||||
|       if (textset.translators?.data.length === 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Missing translators attribution", | ||||
| @ -365,7 +399,7 @@ function useTesting(props: Props) { | ||||
|           contentURL | ||||
|         ); | ||||
|       } | ||||
|       if (textset.transcribers.data.length > 0) { | ||||
|       if (textset.transcribers && textset.transcribers.data.length > 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Translations shouldn't have transcribers", | ||||
|  | ||||
| @ -1,379 +0,0 @@ | ||||
| import AppLayout from "components/AppLayout"; | ||||
| import Button from "components/Button"; | ||||
| 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 { getContentsSlugs, getContentText } from "graphql/operations"; | ||||
| import { GetContentTextQuery } from "graphql/operations-types"; | ||||
| import { | ||||
|   GetStaticPathsContext, | ||||
|   GetStaticPathsResult, | ||||
|   GetStaticPropsContext, | ||||
| } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { | ||||
|   getLocalesFromLanguages, | ||||
|   getStatusDescription, | ||||
|   prettyinlineTitle, | ||||
|   prettyLanguage, | ||||
|   prettySlug, | ||||
|   prettyTestError, | ||||
|   prettyTestWarning, | ||||
| } from "queries/helpers"; | ||||
| 
 | ||||
| interface ContentReadProps extends AppStaticProps { | ||||
|   content: GetContentTextQuery["contents"]["data"][number]["attributes"]; | ||||
|   contentId: GetContentTextQuery["contents"]["data"][number]["id"]; | ||||
| } | ||||
| 
 | ||||
| export default function ContentRead(props: ContentReadProps): JSX.Element { | ||||
|   useTesting(props); | ||||
|   const { langui, content, languages } = props; | ||||
|   const router = useRouter(); | ||||
|   const locales = getLocalesFromLanguages(content.text_set_languages); | ||||
| 
 | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href={`/contents/${content.slug}`} | ||||
|         title={"Content"} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         horizontalLine | ||||
|       /> | ||||
| 
 | ||||
|       {content.text_set.length > 0 && content.text_set[0].source_language.data && ( | ||||
|         <div className="grid gap-5"> | ||||
|           <h2 className="text-xl"> | ||||
|             {content.text_set[0].source_language.data.attributes.code === | ||||
|             router.locale | ||||
|               ? langui.transcript_notice | ||||
|               : langui.translation_notice} | ||||
|           </h2> | ||||
| 
 | ||||
|           {content.text_set[0].source_language.data.attributes.code !== | ||||
|             router.locale && ( | ||||
|             <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 | ||||
|                 } | ||||
|               > | ||||
|                 {prettyLanguage( | ||||
|                   content.text_set[0].source_language.data.attributes.code, | ||||
|                   languages | ||||
|                 )} | ||||
|               </Button> | ||||
|             </div> | ||||
|           )} | ||||
| 
 | ||||
|           <div className="grid grid-flow-col place-items-center place-content-center gap-2"> | ||||
|             <p className="font-headers">{langui.status}:</p> | ||||
| 
 | ||||
|             <ToolTip | ||||
|               content={getStatusDescription(content.text_set[0].status, langui)} | ||||
|               maxWidth={"20rem"} | ||||
|             > | ||||
|               <Chip>{content.text_set[0].status}</Chip> | ||||
|             </ToolTip> | ||||
|           </div> | ||||
| 
 | ||||
|           {content.text_set[0].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) => ( | ||||
|                   <RecorderChip | ||||
|                     key={recorder.id} | ||||
|                     langui={langui} | ||||
|                     recorder={recorder} | ||||
|                   /> | ||||
|                 ))} | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
| 
 | ||||
|           {content.text_set[0].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) => ( | ||||
|                   <RecorderChip | ||||
|                     key={recorder.id} | ||||
|                     langui={langui} | ||||
|                     recorder={recorder} | ||||
|                   /> | ||||
|                 ))} | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
| 
 | ||||
|           {content.text_set[0].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) => ( | ||||
|                   <RecorderChip | ||||
|                     key={recorder.id} | ||||
|                     langui={langui} | ||||
|                     recorder={recorder} | ||||
|                   /> | ||||
|                 ))} | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       {content.text_set.length > 0 && content.text_set[0].text && ( | ||||
|         <> | ||||
|           <HorizontalLine /> | ||||
|           <TOC | ||||
|             text={content.text_set[0].text} | ||||
|             title={ | ||||
|               content.titles.length > 0 | ||||
|                 ? prettyinlineTitle( | ||||
|                     content.titles[0].pre_title, | ||||
|                     content.titles[0].title, | ||||
|                     content.titles[0].subtitle | ||||
|                   ) | ||||
|                 : prettySlug(content.slug) | ||||
|             } | ||||
|           /> | ||||
|         </> | ||||
|       )} | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
|     <ContentPanel> | ||||
|       <ReturnButton | ||||
|         href={`/contents/${content.slug}`} | ||||
|         title={langui.content} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       <div className="grid place-items-center"> | ||||
|         <ThumbnailHeader | ||||
|           thumbnail={content.thumbnail.data?.attributes} | ||||
|           pre_title={ | ||||
|             content.titles.length > 0 ? content.titles[0].pre_title : undefined | ||||
|           } | ||||
|           title={ | ||||
|             content.titles.length > 0 | ||||
|               ? content.titles[0].title | ||||
|               : prettySlug(content.slug) | ||||
|           } | ||||
|           subtitle={ | ||||
|             content.titles.length > 0 ? content.titles[0].subtitle : undefined | ||||
|           } | ||||
|           description={ | ||||
|             content.titles.length > 0 | ||||
|               ? content.titles[0].description | ||||
|               : undefined | ||||
|           } | ||||
|           type={content.type} | ||||
|           categories={content.categories} | ||||
|           langui={langui} | ||||
|         /> | ||||
| 
 | ||||
|         <HorizontalLine /> | ||||
| 
 | ||||
|         {locales.includes(router.locale ?? "en") ? ( | ||||
|           <Markdawn text={content.text_set[0].text} /> | ||||
|         ) : ( | ||||
|           <LanguageSwitcher | ||||
|             locales={locales} | ||||
|             languages={props.languages} | ||||
|             langui={props.langui} | ||||
|           /> | ||||
|         )} | ||||
|       </div> | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   let description = ""; | ||||
|   if (content.type.data) { | ||||
|     description += `${langui.type}: `; | ||||
|     if (content.type.data.attributes.titles.length > 0) { | ||||
|       description += content.type.data.attributes.titles[0].title; | ||||
|     } else { | ||||
|       description += prettySlug(content.type.data.attributes.slug); | ||||
|     } | ||||
|     description += "\n"; | ||||
|   } | ||||
|   if (content.categories.data.length > 0) { | ||||
|     description += `${langui.categories}: `; | ||||
|     description += content.categories.data | ||||
|       .map((category) => category.attributes.short) | ||||
|       .join(" | "); | ||||
|     description += "\n"; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle="Contents" | ||||
|       title={ | ||||
|         content.titles.length > 0 | ||||
|           ? prettyinlineTitle( | ||||
|               content.titles[0].pre_title, | ||||
|               content.titles[0].title, | ||||
|               content.titles[0].subtitle | ||||
|             ) | ||||
|           : prettySlug(content.slug) | ||||
|       } | ||||
|       thumbnail={content.thumbnail.data?.attributes} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       description={description} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: ContentReadProps }> { | ||||
|   const slug = context.params?.slug?.toString() ?? ""; | ||||
|   const content = ( | ||||
|     await getContentText({ | ||||
|       slug: slug, | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).contents.data[0]; | ||||
|   if (!content) return { notFound: true }; | ||||
|   const props: ContentReadProps = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     content: content.attributes, | ||||
|     contentId: content.id, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export async function getStaticPaths( | ||||
|   context: GetStaticPathsContext | ||||
| ): Promise<GetStaticPathsResult> { | ||||
|   const contents = await getContentsSlugs({}); | ||||
|   const paths: GetStaticPathsResult["paths"] = []; | ||||
|   contents.contents.data.map((item) => { | ||||
|     context.locales?.map((local) => { | ||||
|       paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|     }); | ||||
|   }); | ||||
|   return { | ||||
|     paths, | ||||
|     fallback: "blocking", | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function useTesting(props: ContentReadProps) { | ||||
|   const router = useRouter(); | ||||
|   const { content, contentId } = props; | ||||
| 
 | ||||
|   const contentURL = `/admin/content-manager/collectionType/api::content.content/${contentId}`; | ||||
| 
 | ||||
|   if (router.locale === "en") { | ||||
|     if (content.categories.data.length === 0) { | ||||
|       prettyTestError(router, "Missing categories", ["content"], contentURL); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (content.ranged_contents.data.length === 0) { | ||||
|     prettyTestWarning( | ||||
|       router, | ||||
|       "Unconnected to any source", | ||||
|       ["content"], | ||||
|       contentURL | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (content.text_set.length === 0) { | ||||
|     prettyTestWarning( | ||||
|       router, | ||||
|       "Has no textset, nor audioset, nor videoset", | ||||
|       ["content"], | ||||
|       contentURL | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (content.text_set.length > 1) { | ||||
|     prettyTestError( | ||||
|       router, | ||||
|       "More than one textset for this language", | ||||
|       ["content", "text_set"], | ||||
|       contentURL | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (content.text_set.length === 1) { | ||||
|     const textset = content.text_set[0]; | ||||
| 
 | ||||
|     if (!textset.text) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing text", | ||||
|         ["content", "text_set"], | ||||
|         contentURL | ||||
|       ); | ||||
|     } | ||||
|     if (!textset.source_language.data) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing source language", | ||||
|         ["content", "text_set"], | ||||
|         contentURL | ||||
|       ); | ||||
|     } else if (textset.source_language.data.attributes.code === router.locale) { | ||||
|       // This is a transcript
 | ||||
|       if (textset.transcribers.data.length === 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Missing transcribers attribution", | ||||
|           ["content", "text_set"], | ||||
|           contentURL | ||||
|         ); | ||||
|       } | ||||
|       if (textset.translators.data.length > 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Transcripts shouldn't have translators", | ||||
|           ["content", "text_set"], | ||||
|           contentURL | ||||
|         ); | ||||
|       } | ||||
|     } else { | ||||
|       // This is a translation
 | ||||
|       if (textset.translators.data.length === 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Missing translators attribution", | ||||
|           ["content", "text_set"], | ||||
|           contentURL | ||||
|         ); | ||||
|       } | ||||
|       if (textset.transcribers.data.length > 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Translations shouldn't have transcribers", | ||||
|           ["content", "text_set"], | ||||
|           contentURL | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -7,21 +7,18 @@ import ContentPanel, { | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import Select from "components/Select"; | ||||
| import { getContents } from "graphql/operations"; | ||||
| import { | ||||
|   GetContentsQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { GetContentsQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyinlineTitle, prettySlug } from "queries/helpers"; | ||||
| import { useEffect, useState } from "react"; | ||||
| 
 | ||||
| interface ContentsProps extends AppStaticProps { | ||||
|   contents: GetContentsQuery["contents"]["data"]; | ||||
|   contents: Exclude<GetContentsQuery["contents"], null | undefined>["data"]; | ||||
| } | ||||
| 
 | ||||
| type GroupContentItems = Map<string, GetContentsQuery["contents"]["data"]>; | ||||
| type GroupContentItems = Map<string, ContentsProps["contents"]>; | ||||
| 
 | ||||
| export default function Contents(props: ContentsProps): JSX.Element { | ||||
|   const { langui, contents } = props; | ||||
| @ -48,7 +45,7 @@ export default function Contents(props: ContentsProps): JSX.Element { | ||||
|         <p className="flex-shrink-0">{langui.group_by}:</p> | ||||
|         <Select | ||||
|           className="w-full" | ||||
|           options={[langui.category, langui.type]} | ||||
|           options={[langui.category ?? "", langui.type ?? ""]} | ||||
|           state={groupingMethod} | ||||
|           setState={setGroupingMethod} | ||||
|           allowEmpty | ||||
| @ -70,8 +67,8 @@ export default function Contents(props: ContentsProps): JSX.Element { | ||||
|                   {name} | ||||
|                   <Chip>{`${items.length} ${ | ||||
|                     items.length <= 1 | ||||
|                       ? langui.result.toLowerCase() | ||||
|                       : langui.results.toLowerCase() | ||||
|                       ? langui.result?.toLowerCase() ?? "" | ||||
|                       : langui.results?.toLowerCase() ?? "" | ||||
|                   }`}</Chip>
 | ||||
|                 </h2> | ||||
|               )} | ||||
| @ -80,7 +77,14 @@ export default function Contents(props: ContentsProps): JSX.Element { | ||||
|                 className="grid gap-8 items-end grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]" | ||||
|               > | ||||
|                 {items.map((item) => ( | ||||
|                   <LibraryContentPreview key={item.id} item={item.attributes} /> | ||||
|                   <> | ||||
|                     {item.attributes && ( | ||||
|                       <LibraryContentPreview | ||||
|                         key={item.id} | ||||
|                         item={item.attributes} | ||||
|                       /> | ||||
|                     )} | ||||
|                   </> | ||||
|                 ))} | ||||
|               </div> | ||||
|             </> | ||||
| @ -102,35 +106,32 @@ export default function Contents(props: ContentsProps): JSX.Element { | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: ContentsProps }> { | ||||
|   const contents = ( | ||||
|     await getContents({ | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).contents.data; | ||||
| 
 | ||||
|   contents.sort((a, b) => { | ||||
|     const titleA = | ||||
|       a.attributes.titles.length > 0 | ||||
|         ? prettyinlineTitle( | ||||
|             a.attributes.titles[0].pre_title, | ||||
|             a.attributes.titles[0].title, | ||||
|             a.attributes.titles[0].subtitle | ||||
|           ) | ||||
|         : a.attributes.slug; | ||||
|     const titleB = | ||||
|       b.attributes.titles.length > 0 | ||||
|         ? prettyinlineTitle( | ||||
|             b.attributes.titles[0].pre_title, | ||||
|             b.attributes.titles[0].title, | ||||
|             b.attributes.titles[0].subtitle | ||||
|           ) | ||||
|         : b.attributes.slug; | ||||
|   const sdk = getReadySdk(); | ||||
|   const contents = await sdk.getContents({ | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!contents.contents) return { notFound: true }; | ||||
|   contents.contents.data.sort((a, b) => { | ||||
|     const titleA = a.attributes?.titles?.[0] | ||||
|       ? prettyinlineTitle( | ||||
|           a.attributes.titles[0].pre_title, | ||||
|           a.attributes.titles[0].title, | ||||
|           a.attributes.titles[0].subtitle | ||||
|         ) | ||||
|       : a.attributes?.slug ?? ""; | ||||
|     const titleB = b.attributes?.titles?.[0] | ||||
|       ? prettyinlineTitle( | ||||
|           b.attributes.titles[0].pre_title, | ||||
|           b.attributes.titles[0].title, | ||||
|           b.attributes.titles[0].subtitle | ||||
|         ) | ||||
|       : b.attributes?.slug ?? ""; | ||||
|     return titleA.localeCompare(titleB); | ||||
|   }); | ||||
| 
 | ||||
|   const props: ContentsProps = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     contents: contents, | ||||
|     contents: contents.contents.data, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -138,7 +139,7 @@ export async function getStaticProps( | ||||
| } | ||||
| 
 | ||||
| function getGroups( | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"], | ||||
|   langui: AppStaticProps["langui"], | ||||
|   groupByType: number, | ||||
|   items: ContentsProps["contents"] | ||||
| ): GroupContentItems { | ||||
| @ -165,11 +166,11 @@ function getGroups( | ||||
|       group.set(langui.no_category, []); | ||||
| 
 | ||||
|       items.map((item) => { | ||||
|         if (item.attributes.categories.data.length === 0) { | ||||
|         if (item.attributes?.categories?.data.length === 0) { | ||||
|           group.get(langui.no_category)?.push(item); | ||||
|         } else { | ||||
|           item.attributes.categories.data.map((category) => { | ||||
|             group.get(category.attributes.name)?.push(item); | ||||
|           item.attributes?.categories?.data.map((category) => { | ||||
|             group.get(category.attributes?.name)?.push(item); | ||||
|           }); | ||||
|         } | ||||
|       }); | ||||
| @ -180,10 +181,8 @@ function getGroups( | ||||
|       const group: GroupContentItems = new Map(); | ||||
|       items.map((item) => { | ||||
|         const type = | ||||
|           item.attributes.type.data.attributes.titles.length > 0 | ||||
|             ? item.attributes.type.data.attributes.titles[0].title | ||||
|             : prettySlug(item.attributes.type.data.attributes.slug); | ||||
| 
 | ||||
|           item.attributes?.type?.data?.attributes?.titles?.[0]?.title ?? | ||||
|           prettySlug(item.attributes?.type?.data?.attributes?.slug); | ||||
|         if (!group.has(type)) group.set(type, []); | ||||
|         group.get(type)?.push(item); | ||||
|       }); | ||||
|  | ||||
| @ -2,22 +2,28 @@ import AppLayout from "components/AppLayout"; | ||||
| import LanguageSwitcher from "components/LanguageSwitcher"; | ||||
| import Markdawn from "components/Markdown/Markdawn"; | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import { getPost } from "graphql/operations"; | ||||
| import { GetPostQuery } from "graphql/operations-types"; | ||||
| 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 HomeProps extends AppStaticProps { | ||||
|   post: GetPostQuery["posts"]["data"][number]["attributes"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
| } | ||||
| 
 | ||||
| export default function Home(props: HomeProps): JSX.Element { | ||||
| export default function Home(props: Props): JSX.Element { | ||||
|   const { post } = props; | ||||
|   const locales = getLocalesFromLanguages(post.translations_languages); | ||||
|   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"> | ||||
| @ -28,7 +34,7 @@ export default function Home(props: HomeProps): JSX.Element { | ||||
|         </h2> | ||||
|       </div> | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={post.translations[0].body} /> | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
| @ -39,31 +45,22 @@ export default function Home(props: HomeProps): JSX.Element { | ||||
|     </ContentPanel> | ||||
|   ); | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={ | ||||
|         post.translations.length > 0 | ||||
|           ? post.translations[0].title | ||||
|           : prettySlug(post.slug) | ||||
|       } | ||||
|       contentPanel={contentPanel} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
|   return <AppLayout navTitle={title} contentPanel={contentPanel} {...props} />; | ||||
| } | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: HomeProps }> { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = "home"; | ||||
|   const props: HomeProps = { | ||||
|   const post = await sdk.getPost({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!post.posts) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     post: ( | ||||
|       await getPost({ | ||||
|         slug: slug, | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).posts.data[0].attributes, | ||||
|     post: post.posts.data[0].attributes, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -15,12 +15,12 @@ import ContentPanel, { | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { getLibraryItem, getLibraryItemsSlugs } from "graphql/operations"; | ||||
| import { | ||||
|   Enum_Componentmetadatabooks_Binding_Type, | ||||
|   Enum_Componentmetadatabooks_Page_Order, | ||||
|   GetLibraryItemQuery, | ||||
| } from "graphql/operations-types"; | ||||
| } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { | ||||
|   GetStaticPathsContext, | ||||
|   GetStaticPathsResult, | ||||
| @ -41,22 +41,27 @@ import { | ||||
| } from "queries/helpers"; | ||||
| import { useState } from "react"; | ||||
| 
 | ||||
| interface LibrarySlugProps extends AppStaticProps { | ||||
|   item: GetLibraryItemQuery["libraryItems"]["data"][number]["attributes"]; | ||||
|   itemId: GetLibraryItemQuery["libraryItems"]["data"][number]["id"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   item: Exclude< | ||||
|     GetLibraryItemQuery["libraryItems"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
|   itemId: Exclude< | ||||
|     GetLibraryItemQuery["libraryItems"], | ||||
|     null | undefined | ||||
|   >["data"][number]["id"]; | ||||
| } | ||||
| 
 | ||||
| export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
| export default function LibrarySlug(props: Props): JSX.Element { | ||||
|   useTesting(props); | ||||
|   const { item, langui, currencies } = props; | ||||
|   const appLayout = useAppLayout(); | ||||
| 
 | ||||
|   const isVariantSet = | ||||
|     item.metadata.length > 0 && | ||||
|     item.metadata[0].__typename === "ComponentMetadataGroup" && | ||||
|     item.metadata[0].subtype.data.attributes.slug === "variant-set"; | ||||
|     item?.metadata?.[0]?.__typename === "ComponentMetadataGroup" && | ||||
|     item.metadata[0].subtype?.data?.attributes?.slug === "variant-set"; | ||||
| 
 | ||||
|   sortContent(item.contents); | ||||
|   sortContent(item?.contents); | ||||
| 
 | ||||
|   const [lightboxOpen, setLightboxOpen] = useState(false); | ||||
|   const [lightboxImages, setLightboxImages] = useState([""]); | ||||
| @ -80,7 +85,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|           onClick={() => appLayout.setSubPanelOpen(false)} | ||||
|         /> | ||||
| 
 | ||||
|         {item.gallery.data.length > 0 && ( | ||||
|         {item?.gallery && item.gallery.data.length > 0 && ( | ||||
|           <NavOption | ||||
|             title={langui.gallery} | ||||
|             url="#gallery" | ||||
| @ -96,7 +101,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|           onClick={() => appLayout.setSubPanelOpen(false)} | ||||
|         /> | ||||
| 
 | ||||
|         {item.subitems.data.length > 0 && ( | ||||
|         {item?.subitems && item.subitems.data.length > 0 && ( | ||||
|           <NavOption | ||||
|             title={isVariantSet ? langui.variants : langui.subitems} | ||||
|             url={isVariantSet ? "#variants" : "#subitems"} | ||||
| @ -105,7 +110,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|           /> | ||||
|         )} | ||||
| 
 | ||||
|         {item.contents.data.length > 0 && ( | ||||
|         {item?.contents && item.contents.data.length > 0 && ( | ||||
|           <NavOption | ||||
|             title={langui.contents} | ||||
|             url="#contents" | ||||
| @ -138,17 +143,19 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|         <div | ||||
|           className="drop-shadow-shade-xl w-full h-[50vh] mobile:h-[60vh] desktop:mb-16 relative cursor-pointer" | ||||
|           onClick={() => { | ||||
|             setLightboxOpen(true); | ||||
|             setLightboxImages([ | ||||
|               getAssetURL( | ||||
|                 item.thumbnail.data.attributes.url, | ||||
|                 ImageQuality.Large | ||||
|               ), | ||||
|             ]); | ||||
|             setLightboxIndex(0); | ||||
|             if (item?.thumbnail?.data?.attributes) { | ||||
|               setLightboxOpen(true); | ||||
|               setLightboxImages([ | ||||
|                 getAssetURL( | ||||
|                   item.thumbnail.data.attributes.url, | ||||
|                   ImageQuality.Large | ||||
|                 ), | ||||
|               ]); | ||||
|               setLightboxIndex(0); | ||||
|             } | ||||
|           }} | ||||
|         > | ||||
|           {item.thumbnail.data ? ( | ||||
|           {item?.thumbnail?.data?.attributes ? ( | ||||
|             <Img | ||||
|               image={item.thumbnail.data.attributes} | ||||
|               quality={ImageQuality.Large} | ||||
| @ -163,7 +170,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
| 
 | ||||
|         <InsetBox id="summary" className="grid place-items-center"> | ||||
|           <div className="w-[clamp(0px,100%,42rem)] grid place-items-center gap-8"> | ||||
|             {item.subitem_of.data.length > 0 && ( | ||||
|             {item?.subitem_of?.data[0]?.attributes && ( | ||||
|               <div className="grid place-items-center"> | ||||
|                 <p>{langui.subitem_of}</p> | ||||
|                 <Button | ||||
| @ -178,41 +185,53 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|               </div> | ||||
|             )} | ||||
|             <div className="grid place-items-center"> | ||||
|               <h1 className="text-3xl">{item.title}</h1> | ||||
|               {item.subtitle && <h2 className="text-2xl">{item.subtitle}</h2>} | ||||
|               <h1 className="text-3xl">{item?.title}</h1> | ||||
|               {item?.subtitle && <h2 className="text-2xl">{item.subtitle}</h2>} | ||||
|             </div> | ||||
|             {item.descriptions.length > 0 && ( | ||||
|             {item?.descriptions?.[0] && ( | ||||
|               <p className="text-justify">{item.descriptions[0].description}</p> | ||||
|             )} | ||||
|           </div> | ||||
|         </InsetBox> | ||||
| 
 | ||||
|         {item.gallery.data.length > 0 && ( | ||||
|         {item?.gallery && item.gallery.data.length > 0 && ( | ||||
|           <div id="gallery" className="grid place-items-center gap-8  w-full"> | ||||
|             <h2 className="text-2xl">{langui.gallery}</h2> | ||||
|             <div className="grid w-full gap-8 items-end grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]"> | ||||
|               {item.gallery.data.map((galleryItem, index) => ( | ||||
|                 <div | ||||
|                   key={galleryItem.id} | ||||
|                   className="relative aspect-square hover:scale-[1.02] transition-transform cursor-pointer" | ||||
|                   onClick={() => { | ||||
|                     setLightboxOpen(true); | ||||
|                     setLightboxImages( | ||||
|                       item.gallery.data.map((image) => | ||||
|                         getAssetURL(image.attributes.url, ImageQuality.Large) | ||||
|                       ) | ||||
|                     ); | ||||
|                     setLightboxIndex(index); | ||||
|                   }} | ||||
|                 > | ||||
|                   <div className="bg-light absolute inset-0 rounded-lg drop-shadow-shade-md"></div> | ||||
|                   <Img | ||||
|                     className="rounded-lg" | ||||
|                     image={galleryItem.attributes} | ||||
|                     layout="fill" | ||||
|                     objectFit="cover" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <> | ||||
|                   {galleryItem.attributes && ( | ||||
|                     <div | ||||
|                       key={galleryItem.id} | ||||
|                       className="relative aspect-square hover:scale-[1.02] transition-transform cursor-pointer" | ||||
|                       onClick={() => { | ||||
|                         if (item.gallery?.data) { | ||||
|                           const images: string[] = []; | ||||
|                           item.gallery.data.map((image) => { | ||||
|                             if (image.attributes) | ||||
|                               images.push( | ||||
|                                 getAssetURL( | ||||
|                                   image.attributes.url, | ||||
|                                   ImageQuality.Large | ||||
|                                 ) | ||||
|                               ); | ||||
|                           }); | ||||
|                           setLightboxOpen(true); | ||||
|                           setLightboxImages(images); | ||||
|                           setLightboxIndex(index); | ||||
|                         } | ||||
|                       }} | ||||
|                     > | ||||
|                       <div className="bg-light absolute inset-0 rounded-lg drop-shadow-shade-md"></div> | ||||
|                       <Img | ||||
|                         className="rounded-lg" | ||||
|                         image={galleryItem.attributes} | ||||
|                         layout="fill" | ||||
|                         objectFit="cover" | ||||
|                       /> | ||||
|                     </div> | ||||
|                   )} | ||||
|                 </> | ||||
|               ))} | ||||
|             </div> | ||||
|           </div> | ||||
| @ -222,7 +241,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|           <div className="w-[clamp(0px,100%,42rem)] grid place-items gap-8"> | ||||
|             <h2 className="text-2xl text-center">{langui.details}</h2> | ||||
|             <div className="grid grid-flow-col w-full place-content-between"> | ||||
|               {item.metadata.length > 0 && ( | ||||
|               {item?.metadata?.[0] && ( | ||||
|                 <div className="grid place-items-center place-content-start"> | ||||
|                   <h3 className="text-xl">{langui.type}</h3> | ||||
|                   <div className="grid grid-flow-col gap-1"> | ||||
| @ -233,24 +252,24 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|                 </div> | ||||
|               )} | ||||
| 
 | ||||
|               {item.release_date && ( | ||||
|               {item?.release_date && ( | ||||
|                 <div className="grid place-items-center place-content-start"> | ||||
|                   <h3 className="text-xl">{langui.release_date}</h3> | ||||
|                   <p>{prettyDate(item.release_date)}</p> | ||||
|                 </div> | ||||
|               )} | ||||
| 
 | ||||
|               {item.price && ( | ||||
|               {item?.price && ( | ||||
|                 <div className="grid place-items-center text-center place-content-start"> | ||||
|                   <h3 className="text-xl">{langui.price}</h3> | ||||
|                   <p> | ||||
|                     {prettyPrice( | ||||
|                       item.price, | ||||
|                       currencies, | ||||
|                       item.price.currency.data.attributes.code | ||||
|                       item.price.currency?.data?.attributes?.code | ||||
|                     )} | ||||
|                   </p> | ||||
|                   {item.price.currency.data.attributes.code !== | ||||
|                   {item.price.currency?.data?.attributes?.code !== | ||||
|                     appLayout.currency && ( | ||||
|                     <p> | ||||
|                       {prettyPrice(item.price, currencies, appLayout.currency)}{" "} | ||||
| @ -261,18 +280,18 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|               )} | ||||
|             </div> | ||||
| 
 | ||||
|             {item.categories.data.length > 0 && ( | ||||
|             {item?.categories && item.categories.data.length > 0 && ( | ||||
|               <div className="flex flex-col place-items-center gap-2"> | ||||
|                 <h3 className="text-xl">{langui.categories}</h3> | ||||
|                 <div className="flex flex-row flex-wrap place-content-center gap-2"> | ||||
|                   {item.categories.data.map((category) => ( | ||||
|                     <Chip key={category.id}>{category.attributes.name}</Chip> | ||||
|                     <Chip key={category.id}>{category.attributes?.name}</Chip> | ||||
|                   ))} | ||||
|                 </div> | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|             {item.size && ( | ||||
|             {item?.size && ( | ||||
|               <> | ||||
|                 <h3 className="text-xl">{langui.size}</h3> | ||||
|                 <div className="grid grid-flow-col w-full place-content-between"> | ||||
| @ -303,13 +322,12 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|               </> | ||||
|             )} | ||||
| 
 | ||||
|             {item.metadata.length > 0 && | ||||
|               item.metadata[0].__typename !== "ComponentMetadataGroup" && | ||||
|               item.metadata[0].__typename !== "ComponentMetadataOther" && ( | ||||
|             {item?.metadata?.[0]?.__typename !== "ComponentMetadataGroup" && | ||||
|               item?.metadata?.[0]?.__typename !== "ComponentMetadataOther" && ( | ||||
|                 <> | ||||
|                   <h3 className="text-xl">{langui.type_information}</h3> | ||||
|                   <div className="grid grid-cols-2 w-full place-content-between"> | ||||
|                     {item.metadata[0].__typename === | ||||
|                     {item?.metadata?.[0]?.__typename === | ||||
|                       "ComponentMetadataBooks" && ( | ||||
|                       <> | ||||
|                         <div className="flex flex-row place-content-start gap-4"> | ||||
| @ -336,18 +354,15 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|                             {item.metadata[0].page_order === | ||||
|                             Enum_Componentmetadatabooks_Page_Order.LeftToRight | ||||
|                               ? langui.left_to_right | ||||
|                               : item.metadata[0].page_order === | ||||
|                                 Enum_Componentmetadatabooks_Page_Order.RightToLeft | ||||
|                               ? langui.right_to_left | ||||
|                               : ""} | ||||
|                               : langui.right_to_left} | ||||
|                           </p> | ||||
|                         </div> | ||||
| 
 | ||||
|                         <div className="flex flex-row place-content-start gap-4"> | ||||
|                           <p className="font-bold">{langui.languages}:</p> | ||||
|                           {item.metadata[0].languages.data.map((lang) => ( | ||||
|                             <p key={lang.attributes.code}> | ||||
|                               {lang.attributes.name} | ||||
|                           {item.metadata[0]?.languages?.data.map((lang) => ( | ||||
|                             <p key={lang.attributes?.code}> | ||||
|                               {lang.attributes?.name} | ||||
|                             </p> | ||||
|                           ))} | ||||
|                         </div> | ||||
| @ -359,7 +374,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|           </div> | ||||
|         </InsetBox> | ||||
| 
 | ||||
|         {item.subitems.data.length > 0 && ( | ||||
|         {item?.subitems && item.subitems.data.length > 0 && ( | ||||
|           <div | ||||
|             id={isVariantSet ? "variants" : "subitems"} | ||||
|             className="grid place-items-center gap-8 w-full" | ||||
| @ -372,13 +387,14 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
|                 <LibraryItemsPreview | ||||
|                   key={subitem.id} | ||||
|                   item={subitem.attributes} | ||||
|                   currencies={props.currencies} | ||||
|                 /> | ||||
|               ))} | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
| 
 | ||||
|         {item.contents.data.length > 0 && ( | ||||
|         {item?.contents && item.contents.data.length > 0 && ( | ||||
|           <div id="contents" className="w-full grid place-items-center gap-8"> | ||||
|             <h2 className="text-2xl">{langui.contents}</h2> | ||||
|             <div className="grid gap-4 w-full"> | ||||
| @ -399,15 +415,11 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={prettyinlineTitle("", item.title, item.subtitle)} | ||||
|       navTitle={prettyinlineTitle("", item?.title, item?.subtitle)} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       thumbnail={item.thumbnail.data?.attributes} | ||||
|       description={ | ||||
|         item.descriptions.length > 0 | ||||
|           ? item.descriptions[0].description | ||||
|           : undefined | ||||
|       } | ||||
|       thumbnail={item?.thumbnail?.data?.attributes ?? undefined} | ||||
|       description={item?.descriptions?.[0]?.description ?? undefined} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
| @ -415,18 +427,17 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: LibrarySlugProps }> { | ||||
|   const item = ( | ||||
|     await getLibraryItem({ | ||||
|       slug: context.params?.slug?.toString() ?? "", | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).libraryItems.data[0]; | ||||
|   if (!item) return { notFound: true }; | ||||
|   const props: LibrarySlugProps = { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const item = await sdk.getLibraryItem({ | ||||
|     slug: context.params?.slug ? context.params.slug.toString() : "", | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!item.libraryItems) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     item: item.attributes, | ||||
|     itemId: item.id, | ||||
|     item: item.libraryItems.data[0].attributes, | ||||
|     itemId: item.libraryItems.data[0].id, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -436,29 +447,32 @@ export async function getStaticProps( | ||||
| export async function getStaticPaths( | ||||
|   context: GetStaticPathsContext | ||||
| ): Promise<GetStaticPathsResult> { | ||||
|   const libraryItems = await getLibraryItemsSlugs({}); | ||||
|   const sdk = getReadySdk(); | ||||
|   const libraryItems = await sdk.getLibraryItemsSlugs(); | ||||
|   const paths: GetStaticPathsResult["paths"] = []; | ||||
|   libraryItems.libraryItems.data.map((item) => { | ||||
|     context.locales?.map((local) => { | ||||
|       paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|   if (libraryItems.libraryItems) { | ||||
|     libraryItems.libraryItems.data.map((item) => { | ||||
|       context.locales?.map((local) => { | ||||
|         paths.push({ params: { slug: item.attributes?.slug }, locale: local }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|   } | ||||
|   return { | ||||
|     paths, | ||||
|     fallback: "blocking", | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function useTesting(props: LibrarySlugProps) { | ||||
| function useTesting(props: Props) { | ||||
|   const { item, itemId } = props; | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const libraryItemURL = `/admin/content-manager/collectionType/api::library-item.library-item/${itemId}`; | ||||
| 
 | ||||
|   sortContent(item.contents); | ||||
|   sortContent(item?.contents); | ||||
| 
 | ||||
|   if (router.locale === "en") { | ||||
|     if (!item.thumbnail.data) { | ||||
|     if (!item?.thumbnail?.data) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing thumbnail", | ||||
| @ -466,7 +480,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         libraryItemURL | ||||
|       ); | ||||
|     } | ||||
|     if (item.metadata.length === 0) { | ||||
|     if (item?.metadata?.length === 0) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing metadata", | ||||
| @ -474,9 +488,9 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         libraryItemURL | ||||
|       ); | ||||
|     } else if ( | ||||
|       item.metadata[0].__typename === "ComponentMetadataGroup" && | ||||
|       (item.metadata[0].subtype.data.attributes.slug === "relation-set" || | ||||
|         item.metadata[0].subtype.data.attributes.slug === "variant-set") | ||||
|       item?.metadata?.[0]?.__typename === "ComponentMetadataGroup" && | ||||
|       (item.metadata[0].subtype?.data?.attributes?.slug === "relation-set" || | ||||
|         item.metadata[0].subtype?.data?.attributes?.slug === "variant-set") | ||||
|     ) { | ||||
|       // This is a group type item
 | ||||
|       if (item.price) { | ||||
| @ -503,7 +517,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|           libraryItemURL | ||||
|         ); | ||||
|       } | ||||
|       if (item.contents.data.length > 0) { | ||||
|       if (item.contents && item.contents.data.length > 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Group-type items shouldn't have contents", | ||||
| @ -511,7 +525,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|           libraryItemURL | ||||
|         ); | ||||
|       } | ||||
|       if (item.subitems.data.length === 0) { | ||||
|       if (item.subitems && item.subitems.data.length === 0) { | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Group-type items should have subitems", | ||||
| @ -522,8 +536,8 @@ function useTesting(props: LibrarySlugProps) { | ||||
|     } else { | ||||
|       // This is a normal item
 | ||||
| 
 | ||||
|       if (item.metadata[0].__typename === "ComponentMetadataGroup") { | ||||
|         if (item.subitems.data.length === 0) { | ||||
|       if (item?.metadata?.[0]?.__typename === "ComponentMetadataGroup") { | ||||
|         if (item.subitems?.data.length === 0) { | ||||
|           prettyTestError( | ||||
|             router, | ||||
|             "Group-type item should have subitems", | ||||
| @ -533,7 +547,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       if (item.price) { | ||||
|       if (item?.price) { | ||||
|         if (!item.price.amount) { | ||||
|           prettyTestError( | ||||
|             router, | ||||
| @ -559,8 +573,8 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       if (!item.digital) { | ||||
|         if (item.size) { | ||||
|       if (!item?.digital) { | ||||
|         if (item?.size) { | ||||
|           if (!item.size.width) { | ||||
|             prettyTestWarning( | ||||
|               router, | ||||
| @ -595,7 +609,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       if (item.release_date) { | ||||
|       if (item?.release_date) { | ||||
|         if (!item.release_date.year) { | ||||
|           prettyTestError( | ||||
|             router, | ||||
| @ -629,7 +643,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       if (item.contents.data.length === 0) { | ||||
|       if (item?.contents?.data.length === 0) { | ||||
|         prettyTestWarning( | ||||
|           router, | ||||
|           "Missing contents", | ||||
| @ -638,26 +652,27 @@ function useTesting(props: LibrarySlugProps) { | ||||
|         ); | ||||
|       } else { | ||||
|         let currentRangePage = 0; | ||||
|         item.contents.data.map((content) => { | ||||
|         item?.contents?.data.map((content) => { | ||||
|           const contentURL = `/admin/content-manager/collectionType/api::content.content/${content.id}`; | ||||
| 
 | ||||
|           if (content.attributes.scan_set.length === 0) { | ||||
|           if (content.attributes?.scan_set?.length === 0) { | ||||
|             prettyTestWarning( | ||||
|               router, | ||||
|               "Missing scan_set", | ||||
|               ["libraryItem", "content", content.id], | ||||
|               ["libraryItem", "content", content.id ?? ""], | ||||
|               contentURL | ||||
|             ); | ||||
|           } | ||||
|           if (content.attributes.range.length === 0) { | ||||
|           if (content.attributes?.range.length === 0) { | ||||
|             prettyTestWarning( | ||||
|               router, | ||||
|               "Missing range", | ||||
|               ["libraryItem", "content", content.id], | ||||
|               ["libraryItem", "content", content.id ?? ""], | ||||
|               contentURL | ||||
|             ); | ||||
|           } else if ( | ||||
|             content.attributes.range[0].__typename === "ComponentRangePageRange" | ||||
|             content.attributes?.range[0]?.__typename === | ||||
|             "ComponentRangePageRange" | ||||
|           ) { | ||||
|             if ( | ||||
|               content.attributes.range[0].starting_page < | ||||
| @ -666,7 +681,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|               prettyTestError( | ||||
|                 router, | ||||
|                 `Overlapping pages ${content.attributes.range[0].starting_page} to ${currentRangePage}`, | ||||
|                 ["libraryItem", "content", content.id, "range"], | ||||
|                 ["libraryItem", "content", content.id ?? "", "range"], | ||||
|                 libraryItemURL | ||||
|               ); | ||||
|             } else if ( | ||||
| @ -678,16 +693,16 @@ function useTesting(props: LibrarySlugProps) { | ||||
|                 `Missing pages ${currentRangePage + 1} to ${ | ||||
|                   content.attributes.range[0].starting_page - 1 | ||||
|                 }`,
 | ||||
|                 ["libraryItem", "content", content.id, "range"], | ||||
|                 ["libraryItem", "content", content.id ?? "", "range"], | ||||
|                 libraryItemURL | ||||
|               ); | ||||
|             } | ||||
| 
 | ||||
|             if (!content.attributes.content.data) { | ||||
|             if (!content.attributes.content?.data) { | ||||
|               prettyTestWarning( | ||||
|                 router, | ||||
|                 "Missing content", | ||||
|                 ["libraryItem", "content", content.id, "range"], | ||||
|                 ["libraryItem", "content", content.id ?? "", "range"], | ||||
|                 libraryItemURL | ||||
|               ); | ||||
|             } | ||||
| @ -696,26 +711,8 @@ function useTesting(props: LibrarySlugProps) { | ||||
|           } | ||||
|         }); | ||||
| 
 | ||||
|         if (item.metadata[0].__typename === "ComponentMetadataBooks") { | ||||
|           if (currentRangePage < item.metadata[0].page_count) { | ||||
|             prettyTestError( | ||||
|               router, | ||||
|               `Missing pages ${currentRangePage + 1} to ${ | ||||
|                 item.metadata[0].page_count | ||||
|               }`,
 | ||||
|               ["libraryItem", "content"], | ||||
|               libraryItemURL | ||||
|             ); | ||||
|           } else if (currentRangePage > item.metadata[0].page_count) { | ||||
|             prettyTestError( | ||||
|               router, | ||||
|               `Page overflow, content references pages up to ${currentRangePage} when the highest expected was ${item.metadata[0].page_count}`, | ||||
|               ["libraryItem", "content"], | ||||
|               libraryItemURL | ||||
|             ); | ||||
|           } | ||||
| 
 | ||||
|           if (item.metadata[0].languages.data.length === 0) { | ||||
|         if (item?.metadata?.[0]?.__typename === "ComponentMetadataBooks") { | ||||
|           if (item.metadata[0].languages?.data.length === 0) { | ||||
|             prettyTestWarning( | ||||
|               router, | ||||
|               "Missing language", | ||||
| @ -724,7 +721,25 @@ function useTesting(props: LibrarySlugProps) { | ||||
|             ); | ||||
|           } | ||||
| 
 | ||||
|           if (!item.metadata[0].page_count) { | ||||
|           if (item.metadata[0].page_count) { | ||||
|             if (currentRangePage < item.metadata[0].page_count) { | ||||
|               prettyTestError( | ||||
|                 router, | ||||
|                 `Missing pages ${currentRangePage + 1} to ${ | ||||
|                   item.metadata[0].page_count | ||||
|                 }`,
 | ||||
|                 ["libraryItem", "content"], | ||||
|                 libraryItemURL | ||||
|               ); | ||||
|             } else if (currentRangePage > item.metadata[0].page_count) { | ||||
|               prettyTestError( | ||||
|                 router, | ||||
|                 `Page overflow, content references pages up to ${currentRangePage} when the highest expected was ${item.metadata[0].page_count}`, | ||||
|                 ["libraryItem", "content"], | ||||
|                 libraryItemURL | ||||
|               ); | ||||
|             } | ||||
|           } else { | ||||
|             prettyTestWarning( | ||||
|               router, | ||||
|               "Missing page_count", | ||||
| @ -736,7 +751,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (!item.root_item && item.subitem_of.data.length === 0) { | ||||
|     if (!item?.root_item && item?.subitem_of?.data.length === 0) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "This item is inaccessible (not root item and not subitem of another item)", | ||||
| @ -745,7 +760,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (item.gallery.data.length === 0) { | ||||
|     if (item?.gallery?.data.length === 0) { | ||||
|       prettyTestWarning( | ||||
|         router, | ||||
|         "Missing gallery", | ||||
| @ -755,7 +770,7 @@ function useTesting(props: LibrarySlugProps) { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (item.descriptions.length === 0) { | ||||
|   if (item?.descriptions?.length === 0) { | ||||
|     prettyTestWarning( | ||||
|       router, | ||||
|       "Missing description", | ||||
|  | ||||
| @ -11,8 +11,8 @@ import ContentPanel, { | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { getLibraryItemScans, getLibraryItemsSlugs } from "graphql/operations"; | ||||
| import { GetLibraryItemScansQuery } from "graphql/operations-types"; | ||||
| import { GetLibraryItemScansQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { | ||||
|   GetStaticPathsContext, | ||||
|   GetStaticPathsResult, | ||||
| @ -23,15 +23,21 @@ import { prettyinlineTitle, prettySlug, sortContent } from "queries/helpers"; | ||||
| import { useState } from "react"; | ||||
| 
 | ||||
| interface Props extends AppStaticProps { | ||||
|   item: GetLibraryItemScansQuery["libraryItems"]["data"][number]["attributes"]; | ||||
|   itemId: GetLibraryItemScansQuery["libraryItems"]["data"][number]["id"]; | ||||
|   item: Exclude< | ||||
|     GetLibraryItemScansQuery["libraryItems"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
|   itemId: Exclude< | ||||
|     GetLibraryItemScansQuery["libraryItems"], | ||||
|     null | undefined | ||||
|   >["data"][number]["id"]; | ||||
| } | ||||
| 
 | ||||
| export default function LibrarySlug(props: Props): JSX.Element { | ||||
|   const { item, langui } = props; | ||||
|   const appLayout = useAppLayout(); | ||||
| 
 | ||||
|   sortContent(item.contents); | ||||
|   sortContent(item?.contents); | ||||
| 
 | ||||
|   const [lightboxOpen, setLightboxOpen] = useState(false); | ||||
|   const [lightboxImages, setLightboxImages] = useState([""]); | ||||
| @ -40,21 +46,21 @@ export default function LibrarySlug(props: Props): JSX.Element { | ||||
|   const subPanel = ( | ||||
|     <SubPanel> | ||||
|       <ReturnButton | ||||
|         href={`/library/${item.slug}`} | ||||
|         href={`/library/${item?.slug}`} | ||||
|         title={langui.item} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.desktop} | ||||
|         horizontalLine | ||||
|       /> | ||||
| 
 | ||||
|       {item.contents.data.map((content) => ( | ||||
|       {item?.contents?.data.map((content) => ( | ||||
|         <NavOption | ||||
|           key={content.id} | ||||
|           url={`#${content.attributes.slug}`} | ||||
|           title={prettySlug(content.attributes.slug, item.slug)} | ||||
|           url={`#${content.attributes?.slug}`} | ||||
|           title={prettySlug(content.attributes?.slug, item.slug)} | ||||
|           subtitle={ | ||||
|             content.attributes.range.length > 0 && | ||||
|             content.attributes.range[0].__typename === "ComponentRangePageRange" | ||||
|             content.attributes?.range[0]?.__typename === | ||||
|             "ComponentRangePageRange" | ||||
|               ? `${content.attributes.range[0].starting_page} → ${content.attributes.range[0].ending_page}` | ||||
|               : undefined | ||||
|           } | ||||
| @ -76,55 +82,68 @@ export default function LibrarySlug(props: Props): JSX.Element { | ||||
|       /> | ||||
| 
 | ||||
|       <ReturnButton | ||||
|         href={`/library/${item.slug}`} | ||||
|         href={`/library/${item?.slug}`} | ||||
|         title={langui.item} | ||||
|         langui={langui} | ||||
|         displayOn={ReturnButtonType.mobile} | ||||
|         className="mb-10" | ||||
|       /> | ||||
|       {item.contents.data.map((content) => ( | ||||
|       {item?.contents?.data.map((content) => ( | ||||
|         <> | ||||
|           <h2 | ||||
|             id={content.attributes.slug} | ||||
|             id={content.attributes?.slug} | ||||
|             key={`h2${content.id}`} | ||||
|             className="text-2xl pb-2 pt-10 first-of-type:pt-0 flex flex-row place-items-center gap-2" | ||||
|           > | ||||
|             {prettySlug(content.attributes.slug, item.slug)} | ||||
|             {prettySlug(content.attributes?.slug, item.slug)} | ||||
|           </h2> | ||||
| 
 | ||||
|           {content.attributes.scan_set.length > 0 ? ( | ||||
|           {content.attributes?.scan_set?.[0] ? ( | ||||
|             <div | ||||
|               key={`items${content.id}`} | ||||
|               className="grid gap-8 items-end mobile:grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(10rem,1fr))] pb-12 border-b-[3px] border-dotted last-of-type:border-0" | ||||
|             > | ||||
|               {content.attributes.scan_set[0].pages.data.map((page, index) => ( | ||||
|               {content.attributes.scan_set[0].pages?.data.map((page, index) => ( | ||||
|                 <div | ||||
|                   key={page.id} | ||||
|                   className="drop-shadow-shade-lg hover:scale-[1.02] cursor-pointer transition-transform" | ||||
|                   onClick={() => { | ||||
|                     setLightboxOpen(true); | ||||
|                     setLightboxImages( | ||||
|                       content.attributes.scan_set[0].pages.data.map((image) => | ||||
|                         getAssetURL(image.attributes.url, ImageQuality.Large) | ||||
|                       ) | ||||
|                     ); | ||||
|                     if (content.attributes?.scan_set?.[0]?.pages) { | ||||
|                       const images: string[] = []; | ||||
|                       content.attributes.scan_set[0].pages.data.map((image) => { | ||||
|                         if (image.attributes?.url) | ||||
|                           images.push( | ||||
|                             getAssetURL( | ||||
|                               image.attributes.url, | ||||
|                               ImageQuality.Large | ||||
|                             ) | ||||
|                           ); | ||||
|                       }); | ||||
|                       setLightboxImages(images); | ||||
|                     } | ||||
| 
 | ||||
|                     setLightboxIndex(index); | ||||
|                   }} | ||||
|                 > | ||||
|                   <Img image={page.attributes} quality={ImageQuality.Small} /> | ||||
|                   {page.attributes && ( | ||||
|                     <Img image={page.attributes} quality={ImageQuality.Small} /> | ||||
|                   )} | ||||
|                 </div> | ||||
|               ))} | ||||
|             </div> | ||||
|           ) : ( | ||||
|             <div className="pb-12 border-b-[3px] border-dotted last-of-type:border-0"> | ||||
|               <LanguageSwitcher | ||||
|                 locales={content.attributes.scan_set_languages.map( | ||||
|                   (language) => language.language.data.attributes.code | ||||
|                 )} | ||||
|                 languages={props.languages} | ||||
|                 langui={props.langui} | ||||
|                 href={`#${content.attributes.slug}`} | ||||
|               /> | ||||
|               {content.attributes?.scan_set_languages && ( | ||||
|                 <LanguageSwitcher | ||||
|                   locales={content.attributes.scan_set_languages.map( | ||||
|                     (language) => language?.language?.data?.attributes?.code | ||||
|                   )} | ||||
|                   languages={props.languages} | ||||
|                   langui={props.langui} | ||||
|                   href={`#${content.attributes.slug}`} | ||||
|                 /> | ||||
|               )} | ||||
|             </div> | ||||
|           )} | ||||
|         </> | ||||
| @ -134,10 +153,10 @@ export default function LibrarySlug(props: Props): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={prettyinlineTitle("", item.title, item.subtitle)} | ||||
|       navTitle={prettyinlineTitle("", item?.title, item?.subtitle)} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       thumbnail={item.thumbnail.data?.attributes} | ||||
|       thumbnail={item?.thumbnail?.data?.attributes ?? undefined} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
| @ -146,17 +165,16 @@ export default function LibrarySlug(props: Props): JSX.Element { | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const item = ( | ||||
|     await getLibraryItemScans({ | ||||
|       slug: context.params?.slug?.toString() ?? "", | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).libraryItems.data[0]; | ||||
|   if (!item) return { notFound: true }; | ||||
|   const sdk = getReadySdk(); | ||||
|   const item = await sdk.getLibraryItemScans({ | ||||
|     slug: context.params?.slug?.toString() ?? "", | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!item.libraryItems) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     item: item.attributes, | ||||
|     itemId: item.id, | ||||
|     item: item.libraryItems.data[0].attributes, | ||||
|     itemId: item.libraryItems.data[0].id, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -166,13 +184,17 @@ export async function getStaticProps( | ||||
| export async function getStaticPaths( | ||||
|   context: GetStaticPathsContext | ||||
| ): Promise<GetStaticPathsResult> { | ||||
|   const libraryItems = await getLibraryItemsSlugs({}); | ||||
|   const sdk = getReadySdk(); | ||||
|   const libraryItems = await sdk.getLibraryItemsSlugs({}); | ||||
|   const paths: GetStaticPathsResult["paths"] = []; | ||||
|   libraryItems.libraryItems.data.map((item) => { | ||||
|     context.locales?.map((local) => { | ||||
|       paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|   if (libraryItems.libraryItems) { | ||||
|     libraryItems.libraryItems.data.map((item) => { | ||||
|       context.locales?.map((local) => { | ||||
|         if (item.attributes) | ||||
|           paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|   } | ||||
|   return { | ||||
|     paths, | ||||
|     fallback: "blocking", | ||||
|  | ||||
| @ -8,27 +8,23 @@ import ContentPanel, { | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import Select from "components/Select"; | ||||
| import Switch from "components/Switch"; | ||||
| import { getLibraryItemsPreview } from "graphql/operations"; | ||||
| import { | ||||
|   GetCurrenciesQuery, | ||||
|   GetLibraryItemsPreviewQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { GetLibraryItemsPreviewQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { convertPrice, prettyDate, prettyinlineTitle } from "queries/helpers"; | ||||
| import { useEffect, useState } from "react"; | ||||
| 
 | ||||
| interface LibraryProps extends AppStaticProps { | ||||
|   items: GetLibraryItemsPreviewQuery["libraryItems"]["data"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   items: Exclude< | ||||
|     GetLibraryItemsPreviewQuery["libraryItems"], | ||||
|     null | undefined | ||||
|   >["data"]; | ||||
| } | ||||
| 
 | ||||
| type GroupLibraryItems = Map< | ||||
|   string, | ||||
|   GetLibraryItemsPreviewQuery["libraryItems"]["data"] | ||||
| >; | ||||
| type GroupLibraryItems = Map<string, Props["items"]>; | ||||
| 
 | ||||
| export default function Library(props: LibraryProps): JSX.Element { | ||||
| export default function Library(props: Props): JSX.Element { | ||||
|   const { langui, items: libraryItems, currencies } = props; | ||||
| 
 | ||||
|   const [showSubitems, setShowSubitems] = useState<boolean>(false); | ||||
| @ -37,7 +33,7 @@ export default function Library(props: LibraryProps): JSX.Element { | ||||
|   const [sortingMethod, setSortingMethod] = useState<number>(0); | ||||
|   const [groupingMethod, setGroupingMethod] = useState<number>(-1); | ||||
| 
 | ||||
|   const [filteredItems, setFilteredItems] = useState<LibraryProps["items"]>( | ||||
|   const [filteredItems, setFilteredItems] = useState<Props["items"]>( | ||||
|     filterItems( | ||||
|       showSubitems, | ||||
|       showPrimaryItems, | ||||
| @ -46,7 +42,7 @@ export default function Library(props: LibraryProps): JSX.Element { | ||||
|     ) | ||||
|   ); | ||||
| 
 | ||||
|   const [sortedItems, setSortedItem] = useState<LibraryProps["items"]>( | ||||
|   const [sortedItems, setSortedItem] = useState<Props["items"]>( | ||||
|     sortBy(groupingMethod, filteredItems, currencies) | ||||
|   ); | ||||
| 
 | ||||
| @ -85,7 +81,11 @@ export default function Library(props: LibraryProps): JSX.Element { | ||||
|         <p className="flex-shrink-0">{langui.group_by}:</p> | ||||
|         <Select | ||||
|           className="w-full" | ||||
|           options={[langui.category, langui.type, langui.release_year]} | ||||
|           options={[ | ||||
|             langui.category ?? "Category", | ||||
|             langui.type ?? "Type", | ||||
|             langui.release_year ?? "Year", | ||||
|           ]} | ||||
|           state={groupingMethod} | ||||
|           setState={setGroupingMethod} | ||||
|           allowEmpty | ||||
| @ -96,7 +96,11 @@ export default function Library(props: LibraryProps): JSX.Element { | ||||
|         <p className="flex-shrink-0">{langui.order_by}:</p> | ||||
|         <Select | ||||
|           className="w-full" | ||||
|           options={[langui.name, langui.price, langui.release_date]} | ||||
|           options={[ | ||||
|             langui.name ?? "Name", | ||||
|             langui.price ?? "Price", | ||||
|             langui.release_date ?? "Release date", | ||||
|           ]} | ||||
|           state={sortingMethod} | ||||
|           setState={setSortingMethod} | ||||
|         /> | ||||
| @ -132,8 +136,8 @@ export default function Library(props: LibraryProps): JSX.Element { | ||||
|                   {name} | ||||
|                   <Chip>{`${items.length} ${ | ||||
|                     items.length <= 1 | ||||
|                       ? langui.result.toLowerCase() | ||||
|                       : langui.results.toLowerCase() | ||||
|                       ? langui.result?.toLowerCase() ?? "result" | ||||
|                       : langui.results?.toLowerCase() ?? "results" | ||||
|                   }`}</Chip>
 | ||||
|                 </h2> | ||||
|               )} | ||||
| @ -167,14 +171,15 @@ export default function Library(props: LibraryProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: LibraryProps }> { | ||||
|   const props: LibraryProps = { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const items = await sdk.getLibraryItemsPreview({ | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!items.libraryItems) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     items: ( | ||||
|       await getLibraryItemsPreview({ | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).libraryItems.data, | ||||
|     items: items.libraryItems.data, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -182,9 +187,9 @@ export async function getStaticProps( | ||||
| } | ||||
| 
 | ||||
| function getGroups( | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"], | ||||
|   langui: AppStaticProps["langui"], | ||||
|   groupByType: number, | ||||
|   items: LibraryProps["items"] | ||||
|   items: Props["items"] | ||||
| ): GroupLibraryItems { | ||||
|   switch (groupByType) { | ||||
|     case 0: { | ||||
| @ -209,11 +214,11 @@ function getGroups( | ||||
|       typeGroup.set(langui.no_category, []); | ||||
| 
 | ||||
|       items.map((item) => { | ||||
|         if (item.attributes.categories.data.length === 0) { | ||||
|         if (item.attributes?.categories?.data.length === 0) { | ||||
|           typeGroup.get(langui.no_category)?.push(item); | ||||
|         } else { | ||||
|           item.attributes.categories.data.map((category) => { | ||||
|             typeGroup.get(category.attributes.name)?.push(item); | ||||
|           item.attributes?.categories?.data.map((category) => { | ||||
|             typeGroup.get(category.attributes?.name)?.push(item); | ||||
|           }); | ||||
|         } | ||||
|       }); | ||||
| @ -223,49 +228,50 @@ function getGroups( | ||||
| 
 | ||||
|     case 1: { | ||||
|       const group: GroupLibraryItems = new Map(); | ||||
|       group.set(langui.audio, []); | ||||
|       group.set(langui.game, []); | ||||
|       group.set(langui.textual, []); | ||||
|       group.set(langui.video, []); | ||||
|       group.set(langui.other, []); | ||||
|       group.set(langui.group, []); | ||||
|       group.set(langui.no_type, []); | ||||
|       group.set(langui.audio ?? "Audio", []); | ||||
|       group.set(langui.game ?? "Game", []); | ||||
|       group.set(langui.textual ?? "Textual", []); | ||||
|       group.set(langui.video ?? "Video", []); | ||||
|       group.set(langui.other ?? "Other", []); | ||||
|       group.set(langui.group ?? "Group", []); | ||||
|       group.set(langui.no_type ?? "No type", []); | ||||
|       items.map((item) => { | ||||
|         if (item.attributes.metadata.length > 0) { | ||||
|           switch (item.attributes.metadata[0].__typename) { | ||||
|         if (item.attributes?.metadata && item.attributes.metadata.length > 0) { | ||||
|           switch (item.attributes.metadata[0]?.__typename) { | ||||
|             case "ComponentMetadataAudio": | ||||
|               group.get(langui.audio)?.push(item); | ||||
|               group.get(langui.audio ?? "Audio")?.push(item); | ||||
|               break; | ||||
|             case "ComponentMetadataGame": | ||||
|               group.get(langui.game)?.push(item); | ||||
|               group.get(langui.game ?? "Game")?.push(item); | ||||
|               break; | ||||
|             case "ComponentMetadataBooks": | ||||
|               group.get(langui.textual)?.push(item); | ||||
|               group.get(langui.textual ?? "Textual")?.push(item); | ||||
|               break; | ||||
|             case "ComponentMetadataVideo": | ||||
|               group.get(langui.video)?.push(item); | ||||
|               group.get(langui.video ?? "Video")?.push(item); | ||||
|               break; | ||||
|             case "ComponentMetadataOther": | ||||
|               group.get(langui.other)?.push(item); | ||||
|               group.get(langui.other ?? "Other")?.push(item); | ||||
|               break; | ||||
|             case "ComponentMetadataGroup": | ||||
|               switch ( | ||||
|                 item.attributes.metadata[0].subitems_type.data.attributes.slug | ||||
|                 item.attributes.metadata[0]?.subitems_type?.data?.attributes | ||||
|                   ?.slug | ||||
|               ) { | ||||
|                 case "audio": | ||||
|                   group.get(langui.audio)?.push(item); | ||||
|                   group.get(langui.audio ?? "Audio")?.push(item); | ||||
|                   break; | ||||
|                 case "video": | ||||
|                   group.get(langui.video)?.push(item); | ||||
|                   group.get(langui.video ?? "Video")?.push(item); | ||||
|                   break; | ||||
|                 case "game": | ||||
|                   group.get(langui.game)?.push(item); | ||||
|                   group.get(langui.game ?? "Game")?.push(item); | ||||
|                   break; | ||||
|                 case "textual": | ||||
|                   group.get(langui.textual)?.push(item); | ||||
|                   group.get(langui.textual ?? "Textual")?.push(item); | ||||
|                   break; | ||||
|                 case "mixed": | ||||
|                   group.get(langui.group)?.push(item); | ||||
|                   group.get(langui.group ?? "Group")?.push(item); | ||||
|                   break; | ||||
|                 default: { | ||||
|                   throw new Error( | ||||
| @ -279,7 +285,7 @@ function getGroups( | ||||
|             } | ||||
|           } | ||||
|         } else { | ||||
|           group.get(langui.no_type)?.push(item); | ||||
|           group.get(langui.no_type ?? "No type")?.push(item); | ||||
|         } | ||||
|       }); | ||||
|       return group; | ||||
| @ -288,7 +294,7 @@ function getGroups( | ||||
|     case 2: { | ||||
|       const years: number[] = []; | ||||
|       items.map((item) => { | ||||
|         if (item.attributes.release_date) { | ||||
|         if (item.attributes?.release_date?.year) { | ||||
|           if (!years.includes(item.attributes.release_date.year)) | ||||
|             years.push(item.attributes.release_date.year); | ||||
|         } | ||||
| @ -298,12 +304,12 @@ function getGroups( | ||||
|       years.map((year) => { | ||||
|         group.set(year.toString(), []); | ||||
|       }); | ||||
|       group.set(langui.no_year, []); | ||||
|       group.set(langui.no_year || "No year", []); | ||||
|       items.map((item) => { | ||||
|         if (item.attributes.release_date) { | ||||
|         if (item.attributes?.release_date?.year) { | ||||
|           group.get(item.attributes.release_date.year.toString())?.push(item); | ||||
|         } else { | ||||
|           group.get(langui.no_year)?.push(item); | ||||
|           group.get(langui.no_year || "No year")?.push(item); | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
| @ -322,66 +328,63 @@ function filterItems( | ||||
|   showSubitems: boolean, | ||||
|   showPrimaryItems: boolean, | ||||
|   showSecondaryItems: boolean, | ||||
|   items: LibraryProps["items"] | ||||
| ): LibraryProps["items"] { | ||||
|   items: Props["items"] | ||||
| ): Props["items"] { | ||||
|   return [...items].filter((item) => { | ||||
|     if (!showSubitems && !item.attributes.root_item) return false; | ||||
|     if (!showSubitems && !item.attributes?.root_item) return false; | ||||
|     if ( | ||||
|       showSubitems && | ||||
|       item.attributes.metadata.length > 0 && | ||||
|       item.attributes.metadata[0].__typename === "ComponentMetadataGroup" && | ||||
|       (item.attributes.metadata[0].subtype.data.attributes.slug === | ||||
|       item.attributes?.metadata?.[0]?.__typename === "ComponentMetadataGroup" && | ||||
|       (item.attributes.metadata[0].subtype?.data?.attributes?.slug === | ||||
|         "variant-set" || | ||||
|         item.attributes.metadata[0].subtype.data.attributes.slug === | ||||
|         item.attributes.metadata[0].subtype?.data?.attributes?.slug === | ||||
|           "relation-set") | ||||
|     ) | ||||
|       return false; | ||||
|     if (item.attributes.primary && !showPrimaryItems) return false; | ||||
|     if (!item.attributes.primary && !showSecondaryItems) return false; | ||||
|     if (item.attributes?.primary && !showPrimaryItems) return false; | ||||
|     if (!item.attributes?.primary && !showSecondaryItems) return false; | ||||
|     return true; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function sortBy( | ||||
|   orderByType: number, | ||||
|   items: LibraryProps["items"], | ||||
|   currencies: GetCurrenciesQuery["currencies"]["data"] | ||||
| ): LibraryProps["items"] { | ||||
|   items: Props["items"], | ||||
|   currencies: AppStaticProps["currencies"] | ||||
| ): Props["items"] { | ||||
|   switch (orderByType) { | ||||
|     case 0: | ||||
|       return [...items].sort((a, b) => { | ||||
|         const titleA = prettyinlineTitle( | ||||
|           "", | ||||
|           a.attributes.title, | ||||
|           a.attributes.subtitle | ||||
|           a.attributes?.title, | ||||
|           a.attributes?.subtitle | ||||
|         ); | ||||
|         const titleB = prettyinlineTitle( | ||||
|           "", | ||||
|           b.attributes.title, | ||||
|           b.attributes.subtitle | ||||
|           b.attributes?.title, | ||||
|           b.attributes?.subtitle | ||||
|         ); | ||||
|         return titleA.localeCompare(titleB); | ||||
|       }); | ||||
|     case 1: | ||||
|       return [...items].sort((a, b) => { | ||||
|         const priceA = a.attributes.price | ||||
|         const priceA = a.attributes?.price | ||||
|           ? convertPrice(a.attributes.price, currencies[0]) | ||||
|           : 99999; | ||||
|         const priceB = b.attributes.price | ||||
|         const priceB = b.attributes?.price | ||||
|           ? convertPrice(b.attributes.price, currencies[0]) | ||||
|           : 99999; | ||||
|         return priceA - priceB; | ||||
|       }); | ||||
|     case 2: | ||||
|       return [...items].sort((a, b) => { | ||||
|         const dateA = | ||||
|           a.attributes.release_date === null | ||||
|             ? "9999" | ||||
|             : prettyDate(a.attributes.release_date); | ||||
|         const dateB = | ||||
|           b.attributes.release_date === null | ||||
|             ? "9999" | ||||
|             : prettyDate(b.attributes.release_date); | ||||
|         const dateA = a.attributes?.release_date | ||||
|           ? prettyDate(a.attributes.release_date) | ||||
|           : "9999"; | ||||
|         const dateB = b.attributes?.release_date | ||||
|           ? prettyDate(b.attributes.release_date) | ||||
|           : "9999"; | ||||
|         return dateA.localeCompare(dateB); | ||||
|       }); | ||||
|     default: | ||||
|  | ||||
| @ -17,7 +17,13 @@ export default function Merch(props: MerchProps): JSX.Element { | ||||
|     </SubPanel> | ||||
|   ); | ||||
| 
 | ||||
|   return <AppLayout navTitle={langui.merch} subPanel={subPanel} {...props} />; | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={langui.merch} | ||||
|       subPanel={subPanel} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|  | ||||
| @ -12,8 +12,8 @@ import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import RecorderChip from "components/RecorderChip"; | ||||
| import ToolTip from "components/ToolTip"; | ||||
| import { getPost, getPostsSlugs } from "graphql/operations"; | ||||
| import { GetPostQuery, StrapiImage } from "graphql/operations-types"; | ||||
| import { GetPostQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { | ||||
|   GetStaticPathsContext, | ||||
|   GetStaticPathsResult, | ||||
| @ -28,21 +28,30 @@ import { | ||||
| } from "queries/helpers"; | ||||
| 
 | ||||
| interface PostProps extends AppStaticProps { | ||||
|   post: GetPostQuery["posts"]["data"][number]["attributes"]; | ||||
|   postId: GetPostQuery["posts"]["data"][number]["id"]; | ||||
|   post: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["attributes"]; | ||||
|   postId: Exclude< | ||||
|     GetPostQuery["posts"], | ||||
|     null | undefined | ||||
|   >["data"][number]["id"]; | ||||
| } | ||||
| 
 | ||||
| export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
|   const { post, langui } = props; | ||||
|   const locales = getLocalesFromLanguages(post.translations_languages); | ||||
|   const locales = getLocalesFromLanguages(post?.translations_languages); | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const thumbnail: StrapiImage | undefined = | ||||
|     post.translations.length > 0 && post.translations[0].thumbnail.data | ||||
|       ? post.translations[0].thumbnail.data.attributes | ||||
|       : post.thumbnail.data | ||||
|       ? post.thumbnail.data.attributes | ||||
|       : undefined; | ||||
|   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> | ||||
| @ -54,7 +63,7 @@ export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
|         horizontalLine | ||||
|       /> | ||||
| 
 | ||||
|       {post.translations.length > 0 && ( | ||||
|       {post?.translations?.[0] && ( | ||||
|         <div className="grid grid-flow-col place-items-center place-content-center gap-2"> | ||||
|           <p className="font-headers">{langui.status}:</p> | ||||
| 
 | ||||
| @ -67,12 +76,20 @@ export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       {post.authors.data.length > 0 && ( | ||||
|       {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) => ( | ||||
|               <RecorderChip key={author.id} langui={langui} recorder={author} /> | ||||
|               <> | ||||
|                 {author.attributes && ( | ||||
|                   <RecorderChip | ||||
|                     key={author.id} | ||||
|                     langui={langui} | ||||
|                     recorder={author.attributes} | ||||
|                   /> | ||||
|                 )} | ||||
|               </> | ||||
|             ))} | ||||
|           </div> | ||||
|         </div> | ||||
| @ -80,12 +97,7 @@ export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       {post.translations.length > 0 && post.translations[0].body && ( | ||||
|         <TOC | ||||
|           text={post.translations[0].body} | ||||
|           title={post.translations[0].title} | ||||
|         /> | ||||
|       )} | ||||
|       <TOC text={body} title={title} /> | ||||
|     </SubPanel> | ||||
|   ); | ||||
|   const contentPanel = ( | ||||
| @ -100,24 +112,16 @@ export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
| 
 | ||||
|       <ThumbnailHeader | ||||
|         thumbnail={thumbnail} | ||||
|         title={ | ||||
|           post.translations.length > 0 | ||||
|             ? post.translations[0].title | ||||
|             : prettySlug(post.slug) | ||||
|         } | ||||
|         description={ | ||||
|           post.translations.length > 0 | ||||
|             ? post.translations[0].excerpt | ||||
|             : undefined | ||||
|         } | ||||
|         title={title} | ||||
|         description={except} | ||||
|         langui={langui} | ||||
|         categories={post.categories} | ||||
|         categories={post?.categories} | ||||
|       /> | ||||
| 
 | ||||
|       <HorizontalLine /> | ||||
| 
 | ||||
|       {locales.includes(router.locale ?? "en") ? ( | ||||
|         <Markdawn text={post.translations[0].body} /> | ||||
|         <Markdawn text={body} /> | ||||
|       ) : ( | ||||
|         <LanguageSwitcher | ||||
|           locales={locales} | ||||
| @ -130,14 +134,10 @@ export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
| 
 | ||||
|   return ( | ||||
|     <AppLayout | ||||
|       navTitle={ | ||||
|         post.translations.length > 0 | ||||
|           ? post.translations[0].title | ||||
|           : prettySlug(post.slug) | ||||
|       } | ||||
|       navTitle={title} | ||||
|       contentPanel={contentPanel} | ||||
|       subPanel={subPanel} | ||||
|       thumbnail={thumbnail} | ||||
|       thumbnail={thumbnail ?? undefined} | ||||
|       {...props} | ||||
|     /> | ||||
|   ); | ||||
| @ -146,18 +146,17 @@ export default function LibrarySlug(props: PostProps): JSX.Element { | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: PostProps }> { | ||||
|   const slug = context.params?.slug?.toString() ?? ""; | ||||
|   const post = ( | ||||
|     await getPost({ | ||||
|       slug: slug, | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).posts.data[0]; | ||||
|   if (!post) return { notFound: true }; | ||||
|   const sdk = getReadySdk(); | ||||
|   const slug = context.params?.slug ? context.params.slug.toString() : ""; | ||||
|   const post = await sdk.getPost({ | ||||
|     slug: slug, | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!post.posts?.data[0]) return { notFound: true }; | ||||
|   const props: PostProps = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     post: post.attributes, | ||||
|     postId: post.id, | ||||
|     post: post.posts.data[0].attributes, | ||||
|     postId: post.posts.data[0].id, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -167,13 +166,16 @@ export async function getStaticProps( | ||||
| export async function getStaticPaths( | ||||
|   context: GetStaticPathsContext | ||||
| ): Promise<GetStaticPathsResult> { | ||||
|   const posts = await getPostsSlugs({}); | ||||
|   const sdk = getReadySdk(); | ||||
|   const posts = await sdk.getPostsSlugs(); | ||||
|   const paths: GetStaticPathsResult["paths"] = []; | ||||
|   posts.posts.data.map((item) => { | ||||
|     context.locales?.map((local) => { | ||||
|       paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|   if (posts.posts) | ||||
|     posts.posts.data.map((item) => { | ||||
|       context.locales?.map((local) => { | ||||
|         if (item.attributes) | ||||
|           paths.push({ params: { slug: item.attributes.slug }, locale: local }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|   return { | ||||
|     paths, | ||||
|     fallback: "blocking", | ||||
|  | ||||
| @ -5,25 +5,23 @@ import ContentPanel, { | ||||
|   ContentPanelWidthSizes, | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { getPostsPreview } from "graphql/operations"; | ||||
| import { GetPostsPreviewQuery } from "graphql/operations-types"; | ||||
| import { GetPostsPreviewQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| import { prettyDate } from "queries/helpers"; | ||||
| 
 | ||||
| interface NewsProps extends AppStaticProps { | ||||
|   posts: GetPostsPreviewQuery["posts"]["data"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   posts: Exclude<GetPostsPreviewQuery["posts"], null | undefined>["data"]; | ||||
| } | ||||
| 
 | ||||
| export default function News(props: NewsProps): JSX.Element { | ||||
| export default function News(props: Props): JSX.Element { | ||||
|   const { langui, posts } = props; | ||||
| 
 | ||||
|   posts | ||||
|     .sort((a, b) => { | ||||
|       const dateA = | ||||
|         a.attributes.date === null ? "9999" : prettyDate(a.attributes.date); | ||||
|       const dateB = | ||||
|         b.attributes.date === null ? "9999" : prettyDate(b.attributes.date); | ||||
|       const dateA = a.attributes?.date ? prettyDate(a.attributes.date) : "9999"; | ||||
|       const dateB = b.attributes?.date ? prettyDate(b.attributes.date) : "9999"; | ||||
|       return dateA.localeCompare(dateB); | ||||
|     }) | ||||
|     .reverse(); | ||||
| @ -60,12 +58,15 @@ export default function News(props: NewsProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: NewsProps }> { | ||||
|   const props: NewsProps = { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const posts = await sdk.getPostsPreview({ | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!posts.posts) return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     posts: await ( | ||||
|       await getPostsPreview({ language_code: context.locale ?? "en" }) | ||||
|     ).posts.data, | ||||
|     posts: posts.posts.data, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|  | ||||
| @ -8,11 +8,8 @@ import ReturnButton, { | ||||
| import ContentPanel from "components/Panels/ContentPanel"; | ||||
| import SubPanel from "components/Panels/SubPanel"; | ||||
| import { useAppLayout } from "contexts/AppLayoutContext"; | ||||
| import { getChronologyItems, getEras } from "graphql/operations"; | ||||
| import { | ||||
|   GetChronologyItemsQuery, | ||||
|   GetErasQuery, | ||||
| } from "graphql/operations-types"; | ||||
| import { GetChronologyItemsQuery, GetErasQuery } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; | ||||
| @ -22,19 +19,24 @@ import { | ||||
|   prettyTestWarning, | ||||
| } from "queries/helpers"; | ||||
| 
 | ||||
| interface ChronologyProps extends AppStaticProps { | ||||
|   chronologyItems: GetChronologyItemsQuery["chronologyItems"]["data"]; | ||||
|   chronologyEras: GetErasQuery["chronologyEras"]["data"]; | ||||
| interface Props extends AppStaticProps { | ||||
|   chronologyItems: Exclude< | ||||
|     GetChronologyItemsQuery["chronologyItems"], | ||||
|     null | undefined | ||||
|   >["data"]; | ||||
|   chronologyEras: Exclude< | ||||
|     GetErasQuery["chronologyEras"], | ||||
|     null | undefined | ||||
|   >["data"]; | ||||
| } | ||||
| 
 | ||||
| export default function Chronology(props: ChronologyProps): JSX.Element { | ||||
| export default function Chronology(props: Props): JSX.Element { | ||||
|   useTesting(props); | ||||
|   const { chronologyItems, chronologyEras, langui } = props; | ||||
|   const appLayout = useAppLayout(); | ||||
| 
 | ||||
|   // Group by year the Chronology items
 | ||||
|   const chronologyItemYearGroups: GetChronologyItemsQuery["chronologyItems"]["data"][number][][][] = | ||||
|     []; | ||||
|   const chronologyItemYearGroups: Props["chronologyItems"][number][][][] = []; | ||||
| 
 | ||||
|   chronologyEras.map(() => { | ||||
|     chronologyItemYearGroups.push([]); | ||||
| @ -42,25 +44,28 @@ export default function Chronology(props: ChronologyProps): JSX.Element { | ||||
| 
 | ||||
|   let currentChronologyEraIndex = 0; | ||||
|   chronologyItems.map((item) => { | ||||
|     if ( | ||||
|       item.attributes.year > | ||||
|       chronologyEras[currentChronologyEraIndex].attributes.ending_year | ||||
|     ) { | ||||
|       currentChronologyEraIndex += 1; | ||||
|     } | ||||
|     if ( | ||||
|       Object.prototype.hasOwnProperty.call( | ||||
|         chronologyItemYearGroups[currentChronologyEraIndex], | ||||
|         item.attributes.year | ||||
|       ) | ||||
|     ) { | ||||
|       chronologyItemYearGroups[currentChronologyEraIndex][ | ||||
|         item.attributes.year | ||||
|       ].push(item); | ||||
|     } else { | ||||
|       chronologyItemYearGroups[currentChronologyEraIndex][ | ||||
|         item.attributes.year | ||||
|       ] = [item]; | ||||
|     if (item.attributes) { | ||||
|       if ( | ||||
|         item.attributes.year > | ||||
|         (chronologyEras[currentChronologyEraIndex].attributes?.ending_year ?? | ||||
|           999999) | ||||
|       ) { | ||||
|         currentChronologyEraIndex += 1; | ||||
|       } | ||||
|       if ( | ||||
|         Object.prototype.hasOwnProperty.call( | ||||
|           chronologyItemYearGroups[currentChronologyEraIndex], | ||||
|           item.attributes.year | ||||
|         ) | ||||
|       ) { | ||||
|         chronologyItemYearGroups[currentChronologyEraIndex][ | ||||
|           item.attributes.year | ||||
|         ].push(item); | ||||
|       } else { | ||||
|         chronologyItemYearGroups[currentChronologyEraIndex][ | ||||
|           item.attributes.year | ||||
|         ] = [item]; | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
| @ -75,18 +80,24 @@ export default function Chronology(props: ChronologyProps): JSX.Element { | ||||
|       /> | ||||
| 
 | ||||
|       {chronologyEras.map((era) => ( | ||||
|         <NavOption | ||||
|           key={era.id} | ||||
|           url={`#${era.attributes.slug}`} | ||||
|           title={ | ||||
|             era.attributes.title.length > 0 | ||||
|               ? era.attributes.title[0].title | ||||
|               : prettySlug(era.attributes.slug) | ||||
|           } | ||||
|           subtitle={`${era.attributes.starting_year} → ${era.attributes.ending_year}`} | ||||
|           border | ||||
|           onClick={() => appLayout.setSubPanelOpen(false)} | ||||
|         /> | ||||
|         <> | ||||
|           {era.attributes && ( | ||||
|             <NavOption | ||||
|               key={era.id} | ||||
|               url={`#${era.attributes.slug}`} | ||||
|               title={ | ||||
|                 era.attributes.title && | ||||
|                 era.attributes.title.length > 0 && | ||||
|                 era.attributes.title[0] | ||||
|                   ? era.attributes.title[0].title | ||||
|                   : prettySlug(era.attributes.slug) | ||||
|               } | ||||
|               subtitle={`${era.attributes.starting_year} → ${era.attributes.ending_year}`} | ||||
|               border | ||||
|               onClick={() => appLayout.setSubPanelOpen(false)} | ||||
|             /> | ||||
|           )} | ||||
|         </> | ||||
|       ))} | ||||
|     </SubPanel> | ||||
|   ); | ||||
| @ -104,27 +115,31 @@ export default function Chronology(props: ChronologyProps): JSX.Element { | ||||
|       {chronologyItemYearGroups.map((era, eraIndex) => ( | ||||
|         <> | ||||
|           <InsetBox | ||||
|             id={chronologyEras[eraIndex].attributes.slug} | ||||
|             id={chronologyEras[eraIndex].attributes?.slug} | ||||
|             className="grid text-center my-8 gap-4" | ||||
|           > | ||||
|             <h2 className="text-2xl"> | ||||
|               {chronologyEras[eraIndex].attributes.title.length > 0 | ||||
|                 ? chronologyEras[eraIndex].attributes.title[0].title | ||||
|                 : prettySlug(chronologyEras[eraIndex].attributes.slug)} | ||||
|               {chronologyEras[eraIndex].attributes?.title?.[0] | ||||
|                 ? chronologyEras[eraIndex].attributes?.title?.[0]?.title | ||||
|                 : prettySlug(chronologyEras[eraIndex].attributes?.slug)} | ||||
|             </h2> | ||||
|             <p className="whitespace-pre-line "> | ||||
|               {chronologyEras[eraIndex].attributes.title.length > 0 | ||||
|                 ? chronologyEras[eraIndex].attributes.title[0].description | ||||
|               {chronologyEras[eraIndex].attributes?.title?.[0] | ||||
|                 ? chronologyEras[eraIndex].attributes?.title?.[0]?.description | ||||
|                 : ""} | ||||
|             </p> | ||||
|           </InsetBox> | ||||
|           {era.map((items, index) => ( | ||||
|             <ChronologyYearComponent | ||||
|               key={`${eraIndex}-${index}`} | ||||
|               year={items[0].attributes.year} | ||||
|               items={items} | ||||
|               langui={langui} | ||||
|             /> | ||||
|             <> | ||||
|               {items[0].attributes?.year && ( | ||||
|                 <ChronologyYearComponent | ||||
|                   key={`${eraIndex}-${index}`} | ||||
|                   year={items[0].attributes.year} | ||||
|                   items={items} | ||||
|                   langui={langui} | ||||
|                 /> | ||||
|               )} | ||||
|             </> | ||||
|           ))} | ||||
|         </> | ||||
|       ))} | ||||
| @ -143,36 +158,40 @@ export default function Chronology(props: ChronologyProps): JSX.Element { | ||||
| 
 | ||||
| export async function getStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<{ notFound: boolean } | { props: ChronologyProps }> { | ||||
|   const props: ChronologyProps = { | ||||
| ): Promise<{ notFound: boolean } | { props: Props }> { | ||||
|   const sdk = getReadySdk(); | ||||
|   const chronologyItems = await sdk.getChronologyItems({ | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   const chronologyEras = await sdk.getEras({ | ||||
|     language_code: context.locale ?? "en", | ||||
|   }); | ||||
|   if (!chronologyItems.chronologyItems || !chronologyEras.chronologyEras) | ||||
|     return { notFound: true }; | ||||
|   const props: Props = { | ||||
|     ...(await getAppStaticProps(context)), | ||||
|     chronologyItems: ( | ||||
|       await getChronologyItems({ | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).chronologyItems.data, | ||||
|     chronologyEras: (await getEras({ language_code: context.locale ?? "en" })) | ||||
|       .chronologyEras.data, | ||||
|     chronologyItems: chronologyItems.chronologyItems.data, | ||||
|     chronologyEras: chronologyEras.chronologyEras.data, | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function useTesting(props: ChronologyProps) { | ||||
| function useTesting(props: Props) { | ||||
|   const router = useRouter(); | ||||
|   const { chronologyItems, chronologyEras } = props; | ||||
|   chronologyEras.map((era) => { | ||||
|     const chronologyErasURL = `/admin/content-manager/collectionType/api::chronology-era.chronology-era/${chronologyItems[0].id}`; | ||||
| 
 | ||||
|     if (era.attributes.title.length === 0) { | ||||
|     if (era.attributes?.title?.length === 0) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "Missing translation for title and description, using slug instead", | ||||
|         ["chronologyEras", era.attributes.slug], | ||||
|         chronologyErasURL | ||||
|       ); | ||||
|     } else if (era.attributes.title.length > 1) { | ||||
|     } else if (era.attributes?.title && era.attributes.title.length > 1) { | ||||
|       prettyTestError( | ||||
|         router, | ||||
|         "More than one title and description", | ||||
| @ -180,18 +199,18 @@ function useTesting(props: ChronologyProps) { | ||||
|         chronologyErasURL | ||||
|       ); | ||||
|     } else { | ||||
|       if (!era.attributes.title[0].title) | ||||
|       if (!era.attributes?.title?.[0]?.title) | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Missing title, using slug instead", | ||||
|           ["chronologyEras", era.attributes.slug], | ||||
|           ["chronologyEras", era.attributes?.slug ?? ""], | ||||
|           chronologyErasURL | ||||
|         ); | ||||
|       if (!era.attributes.title[0].description) | ||||
|       if (!era.attributes?.title?.[0]?.description) | ||||
|         prettyTestError( | ||||
|           router, | ||||
|           "Missing description", | ||||
|           ["chronologyEras", era.attributes.slug], | ||||
|           ["chronologyEras", era.attributes?.slug ?? ""], | ||||
|           chronologyErasURL | ||||
|         ); | ||||
|     } | ||||
| @ -200,23 +219,23 @@ function useTesting(props: ChronologyProps) { | ||||
|   chronologyItems.map((item) => { | ||||
|     const chronologyItemsURL = `/admin/content-manager/collectionType/api::chronology-item.chronology-item/${chronologyItems[0].id}`; | ||||
| 
 | ||||
|     const date = `${item.attributes.year}/${item.attributes.month}/${item.attributes.day}`; | ||||
|     const date = `${item.attributes?.year}/${item.attributes?.month}/${item.attributes?.day}`; | ||||
| 
 | ||||
|     if (item.attributes.events.length > 0) { | ||||
|     if (item.attributes?.events && item.attributes.events.length > 0) { | ||||
|       item.attributes.events.map((event) => { | ||||
|         if (!event.source.data) { | ||||
|         if (!event?.source?.data) { | ||||
|           prettyTestError( | ||||
|             router, | ||||
|             "No source for this event", | ||||
|             ["chronologyItems", date, event.id], | ||||
|             ["chronologyItems", date, event?.id ?? ""], | ||||
|             chronologyItemsURL | ||||
|           ); | ||||
|         } | ||||
|         if (!(event.translations.length > 0)) { | ||||
|         if (!(event?.translations && event.translations.length > 0)) { | ||||
|           prettyTestWarning( | ||||
|             router, | ||||
|             "No translation for this event", | ||||
|             ["chronologyItems", date, event.id], | ||||
|             ["chronologyItems", date, event?.id ?? ""], | ||||
|             chronologyItemsURL | ||||
|           ); | ||||
|         } | ||||
|  | ||||
| @ -1,39 +1,58 @@ | ||||
| import { | ||||
|   getCurrencies, | ||||
|   getLanguages, | ||||
|   getWebsiteInterface, | ||||
| } from "graphql/operations"; | ||||
| import { | ||||
|   GetCurrenciesQuery, | ||||
|   GetLanguagesQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
| } from "graphql/operations-types"; | ||||
| } from "graphql/generated"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { GetStaticPropsContext } from "next"; | ||||
| 
 | ||||
| export interface AppStaticProps { | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; | ||||
|   currencies: GetCurrenciesQuery["currencies"]["data"]; | ||||
|   languages: GetLanguagesQuery["languages"]["data"]; | ||||
|   langui: Exclude< | ||||
|     Exclude< | ||||
|       GetWebsiteInterfaceQuery["websiteInterfaces"], | ||||
|       null | undefined | ||||
|     >["data"][number]["attributes"], | ||||
|     null | undefined | ||||
|   >; | ||||
|   currencies: Exclude< | ||||
|     GetCurrenciesQuery["currencies"], | ||||
|     null | undefined | ||||
|   >["data"]; | ||||
|   languages: Exclude<GetLanguagesQuery["languages"], null | undefined>["data"]; | ||||
| } | ||||
| 
 | ||||
| export async function getAppStaticProps( | ||||
|   context: GetStaticPropsContext | ||||
| ): Promise<AppStaticProps> { | ||||
|   const languages = (await getLanguages({})).languages.data; | ||||
|   languages.sort((a, b) => | ||||
|     a.attributes.localized_name.localeCompare(b.attributes.localized_name) | ||||
|   ); | ||||
|   const sdk = getReadySdk(); | ||||
|   const languages = (await sdk.getLanguages()).languages; | ||||
| 
 | ||||
|   const currencies = (await getCurrencies({})).currencies.data; | ||||
|   currencies.sort((a, b) => a.attributes.code.localeCompare(b.attributes.code)); | ||||
|   if (languages?.data) { | ||||
|     languages.data.sort((a, b) => | ||||
|       a.attributes && b.attributes | ||||
|         ? a.attributes.localized_name.localeCompare(b.attributes.localized_name) | ||||
|         : 0 | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   const currencies = (await sdk.getCurrencies()).currencies; | ||||
|   if (currencies?.data) { | ||||
|     currencies.data.sort((a, b) => | ||||
|       a.attributes && b.attributes | ||||
|         ? a.attributes.code.localeCompare(b.attributes.code) | ||||
|         : 0 | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   const langui = ( | ||||
|     await sdk.getWebsiteInterface({ | ||||
|       language_code: context.locale ?? "en", | ||||
|     }) | ||||
|   ).websiteInterfaces?.data[0].attributes; | ||||
| 
 | ||||
|   return { | ||||
|     langui: ( | ||||
|       await getWebsiteInterface({ | ||||
|         language_code: context.locale ?? "en", | ||||
|       }) | ||||
|     ).websiteInterfaces.data[0].attributes, | ||||
|     currencies: currencies, | ||||
|     languages: languages, | ||||
|     langui: langui ?? ({} as AppStaticProps["langui"]), | ||||
|     currencies: currencies?.data ?? ({} as AppStaticProps["currencies"]), | ||||
|     languages: languages?.data ?? ({} as AppStaticProps["languages"]), | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @ -4,33 +4,36 @@ import { | ||||
|   ImageQuality, | ||||
| } from "components/Img"; | ||||
| import { | ||||
|   DatePickerFragment, | ||||
|   Enum_Componentsetstextset_Status, | ||||
|   GetCurrenciesQuery, | ||||
|   GetLanguagesQuery, | ||||
|   GetLibraryItemQuery, | ||||
|   GetLibraryItemsPreviewQuery, | ||||
|   GetWebsiteInterfaceQuery, | ||||
|   StrapiImage, | ||||
| } from "graphql/operations-types"; | ||||
|   GetLibraryItemScansQuery, | ||||
|   PricePickerFragment, | ||||
|   UploadImageFragment, | ||||
| } from "graphql/generated"; | ||||
| import { NextRouter } from "next/router"; | ||||
| import { AppStaticProps } from "./getAppStaticProps"; | ||||
| 
 | ||||
| export function prettyDate( | ||||
|   datePicker: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"] | ||||
| ): string { | ||||
|   return `${datePicker.year}/${datePicker.month | ||||
|     .toString() | ||||
|     .padStart(2, "0")}/${datePicker.day.toString().padStart(2, "0")}`;
 | ||||
| export function prettyDate(datePicker: DatePickerFragment): string { | ||||
|   let result = ""; | ||||
|   if (datePicker.year) result += datePicker.year.toString(); | ||||
|   if (datePicker.month) | ||||
|     result += `/${datePicker.month.toString().padStart(2, "0")}`; | ||||
|   if (datePicker.day) | ||||
|     result += `/${datePicker.day.toString().padStart(2, "0")}`; | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| export function prettyPrice( | ||||
|   pricePicker: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["price"], | ||||
|   currencies: GetCurrenciesQuery["currencies"]["data"], | ||||
|   pricePicker: PricePickerFragment, | ||||
|   currencies: AppStaticProps["currencies"], | ||||
|   targetCurrencyCode?: string | ||||
| ): string { | ||||
|   if (!targetCurrencyCode) return ""; | ||||
|   let result = ""; | ||||
|   currencies.map((currency) => { | ||||
|     if (currency.attributes.code === targetCurrencyCode) { | ||||
|     if (currency?.attributes?.code === targetCurrencyCode) { | ||||
|       const amountInTargetCurrency = convertPrice(pricePicker, currency); | ||||
|       result = | ||||
|         currency.attributes.symbol + | ||||
| @ -44,13 +47,22 @@ export function prettyPrice( | ||||
| } | ||||
| 
 | ||||
| export function convertPrice( | ||||
|   pricePicker: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["price"], | ||||
|   targetCurrency: GetCurrenciesQuery["currencies"]["data"][number] | ||||
|   pricePicker: PricePickerFragment, | ||||
|   targetCurrency: Exclude< | ||||
|     GetCurrenciesQuery["currencies"], | ||||
|     null | undefined | ||||
|   >["data"][number] | ||||
| ): number { | ||||
|   return ( | ||||
|     (pricePicker.amount * pricePicker.currency.data.attributes.rate_to_usd) / | ||||
|     targetCurrency.attributes.rate_to_usd | ||||
|   ); | ||||
|   if ( | ||||
|     pricePicker.amount && | ||||
|     pricePicker.currency?.data?.attributes && | ||||
|     targetCurrency.attributes | ||||
|   ) | ||||
|     return ( | ||||
|       (pricePicker.amount * pricePicker.currency.data.attributes.rate_to_usd) / | ||||
|       targetCurrency.attributes.rate_to_usd | ||||
|     ); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| export function prettySlug(slug?: string, parentSlug?: string): string { | ||||
| @ -65,9 +77,9 @@ export function prettySlug(slug?: string, parentSlug?: string): string { | ||||
| } | ||||
| 
 | ||||
| export function prettyinlineTitle( | ||||
|   pretitle: string, | ||||
|   title: string, | ||||
|   subtitle: string | ||||
|   pretitle: string | undefined | null, | ||||
|   title: string | undefined | null, | ||||
|   subtitle: string | undefined | null | ||||
| ): string { | ||||
|   let result = ""; | ||||
|   if (pretitle) result += `${pretitle}: `; | ||||
| @ -77,11 +89,9 @@ export function prettyinlineTitle( | ||||
| } | ||||
| 
 | ||||
| export function prettyItemType( | ||||
|   metadata: { | ||||
|     __typename: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number]["__typename"]; | ||||
|   }, | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"] | ||||
| ): string { | ||||
|   metadata: any, | ||||
|   langui: AppStaticProps["langui"] | ||||
| ): string | undefined | null { | ||||
|   switch (metadata.__typename) { | ||||
|     case "ComponentMetadataAudio": | ||||
|       return langui.audio; | ||||
| @ -100,50 +110,132 @@ export function prettyItemType( | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function prettyItemSubType(metadata: { | ||||
|   /* eslint-disable @typescript-eslint/no-explicit-any */ | ||||
|   __typename: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number]["__typename"]; | ||||
|   subtype?: any; | ||||
|   platforms?: any; | ||||
|   subitems_type?: any; | ||||
| }): string { | ||||
|   switch (metadata.__typename) { | ||||
|     case "ComponentMetadataAudio": | ||||
|     case "ComponentMetadataBooks": | ||||
|     case "ComponentMetadataVideo": | ||||
|       return metadata.subtype.data.attributes.titles.length > 0 | ||||
|         ? metadata.subtype.data.attributes.titles[0].title | ||||
|         : prettySlug(metadata.subtype.data.attributes.slug); | ||||
|     case "ComponentMetadataGame": | ||||
|       return metadata.platforms.data.length > 0 | ||||
|         ? metadata.platforms.data[0].attributes.short | ||||
|         : ""; | ||||
| 
 | ||||
|     case "ComponentMetadataGroup": { | ||||
|       const firstPart = | ||||
|         metadata.subtype.data.attributes.titles.length > 0 | ||||
| export function prettyItemSubType( | ||||
|   metadata: | ||||
|     | { | ||||
|         __typename: "ComponentMetadataAudio"; | ||||
|         subtype?: { | ||||
|           data?: { | ||||
|             attributes?: { | ||||
|               slug: string; | ||||
|               titles?: Array<{ | ||||
|                 title: string; | ||||
|               } | null> | null; | ||||
|             } | null; | ||||
|           } | null; | ||||
|         } | null; | ||||
|       } | ||||
|     | { | ||||
|         __typename: "ComponentMetadataBooks"; | ||||
|         subtype?: { | ||||
|           data?: { | ||||
|             attributes?: { | ||||
|               slug: string; | ||||
|               titles?: Array<{ | ||||
|                 title: string; | ||||
|               } | null> | null; | ||||
|             } | null; | ||||
|           } | null; | ||||
|         } | null; | ||||
|       } | ||||
|     | { | ||||
|         __typename: "ComponentMetadataGame"; | ||||
|         platforms?: { | ||||
|           data: Array<{ | ||||
|             id?: string | null; | ||||
|             attributes?: { | ||||
|               short: string; | ||||
|             } | null; | ||||
|           }>; | ||||
|         } | null; | ||||
|       } | ||||
|     | { | ||||
|         __typename: "ComponentMetadataGroup"; | ||||
|         subtype?: { | ||||
|           data?: { | ||||
|             attributes?: { | ||||
|               slug: string; | ||||
|               titles?: Array<{ | ||||
|                 title: string; | ||||
|               } | null> | null; | ||||
|             } | null; | ||||
|           } | null; | ||||
|         } | null; | ||||
|         subitems_type?: { | ||||
|           data?: { | ||||
|             attributes?: { | ||||
|               slug: string; | ||||
|               titles?: Array<{ | ||||
|                 title: string; | ||||
|               } | null> | null; | ||||
|             } | null; | ||||
|           } | null; | ||||
|         } | null; | ||||
|       } | ||||
|     | { __typename: "ComponentMetadataOther" } | ||||
|     | { | ||||
|         __typename: "ComponentMetadataVideo"; | ||||
|         subtype?: { | ||||
|           data?: { | ||||
|             attributes?: { | ||||
|               slug: string; | ||||
|               titles?: Array<{ | ||||
|                 title: string; | ||||
|               } | null> | null; | ||||
|             } | null; | ||||
|           } | null; | ||||
|         } | null; | ||||
|       } | ||||
|     | { __typename: "Error" } | ||||
|     | null | ||||
| ): string { | ||||
|   if (metadata) { | ||||
|     switch (metadata.__typename) { | ||||
|       case "ComponentMetadataAudio": | ||||
|       case "ComponentMetadataBooks": | ||||
|       case "ComponentMetadataVideo": | ||||
|         return metadata.subtype?.data?.attributes?.titles && | ||||
|           metadata.subtype?.data?.attributes?.titles.length > 0 && | ||||
|           metadata.subtype.data.attributes.titles[0] | ||||
|           ? metadata.subtype.data.attributes.titles[0].title | ||||
|           : prettySlug(metadata.subtype.data.attributes.slug); | ||||
|           : prettySlug(metadata.subtype?.data?.attributes?.slug); | ||||
|       case "ComponentMetadataGame": | ||||
|         return metadata.platforms?.data && | ||||
|           metadata.platforms?.data.length > 0 && | ||||
|           metadata.platforms.data[0].attributes | ||||
|           ? metadata.platforms.data[0].attributes.short | ||||
|           : ""; | ||||
|       case "ComponentMetadataGroup": { | ||||
|         const firstPart = | ||||
|           metadata.subtype?.data?.attributes?.titles && | ||||
|           metadata.subtype?.data?.attributes?.titles.length > 0 && | ||||
|           metadata.subtype.data.attributes.titles[0] | ||||
|             ? metadata.subtype.data.attributes.titles[0].title | ||||
|             : prettySlug(metadata.subtype?.data?.attributes?.slug); | ||||
| 
 | ||||
|       const secondPart = | ||||
|         metadata.subitems_type.data.attributes.titles.length > 0 | ||||
|           ? metadata.subitems_type.data.attributes.titles[0].title | ||||
|           : prettySlug(metadata.subitems_type.data.attributes.slug); | ||||
|       return `${secondPart} ${firstPart}`; | ||||
|         const secondPart = | ||||
|           metadata.subitems_type?.data?.attributes?.titles && | ||||
|           metadata.subitems_type?.data?.attributes?.titles.length > 0 && | ||||
|           metadata.subitems_type.data.attributes.titles[0] | ||||
|             ? metadata.subitems_type.data.attributes.titles[0].title | ||||
|             : prettySlug(metadata.subitems_type?.data?.attributes?.slug); | ||||
|         return `${secondPart} ${firstPart}`; | ||||
|       } | ||||
|       default: | ||||
|         return ""; | ||||
|     } | ||||
|     default: | ||||
|       return ""; | ||||
|   } | ||||
|   return ""; | ||||
|   /* eslint-enable @typescript-eslint/no-explicit-any */ | ||||
| } | ||||
| 
 | ||||
| export function prettyLanguage( | ||||
|   code: string, | ||||
|   languages: GetLanguagesQuery["languages"]["data"] | ||||
|   languages: AppStaticProps["languages"] | ||||
| ): string { | ||||
|   let result = code; | ||||
|   languages.forEach((language) => { | ||||
|     if (language.attributes.code === code) | ||||
|     if (language?.attributes?.code === code) | ||||
|       result = language.attributes.localized_name; | ||||
|   }); | ||||
|   return result; | ||||
| @ -207,8 +299,8 @@ export function capitalizeString(string: string): string { | ||||
|   return words.join(" "); | ||||
| } | ||||
| 
 | ||||
| export function convertMmToInch(mm: number): string { | ||||
|   return (mm * 0.03937008).toPrecision(3); | ||||
| export function convertMmToInch(mm: number | null | undefined): string { | ||||
|   return mm ? (mm * 0.03937008).toPrecision(3) : ""; | ||||
| } | ||||
| 
 | ||||
| export type OgImage = { | ||||
| @ -218,31 +310,44 @@ export type OgImage = { | ||||
|   alt: string; | ||||
| }; | ||||
| 
 | ||||
| export function getOgImage(quality: ImageQuality, image: StrapiImage): OgImage { | ||||
| export function getOgImage( | ||||
|   quality: ImageQuality, | ||||
|   image: UploadImageFragment | ||||
| ): OgImage { | ||||
|   const imgSize = getImgSizesByQuality( | ||||
|     image.width, | ||||
|     image.height, | ||||
|     image.width ?? 0, | ||||
|     image.height ?? 0, | ||||
|     quality ? quality : ImageQuality.Small | ||||
|   ); | ||||
|   return { | ||||
|     image: getAssetURL(image.url, quality), | ||||
|     width: imgSize.width, | ||||
|     height: imgSize.height, | ||||
|     alt: image.alternativeText, | ||||
|     alt: image.alternativeText || "", | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function sortContent(contents: { | ||||
|   data: { | ||||
|     attributes: { | ||||
|       range: GetLibraryItemQuery["libraryItems"]["data"][number]["attributes"]["contents"]["data"][number]["attributes"]["range"]; | ||||
|     }; | ||||
|   }[]; | ||||
| }) { | ||||
|   contents.data.sort((a, b) => { | ||||
| export function sortContent( | ||||
|   contents: | ||||
|     | Exclude< | ||||
|         Exclude< | ||||
|           GetLibraryItemQuery["libraryItems"], | ||||
|           null | undefined | ||||
|         >["data"][number]["attributes"], | ||||
|         null | undefined | ||||
|       >["contents"] | ||||
|     | Exclude< | ||||
|         Exclude< | ||||
|           GetLibraryItemScansQuery["libraryItems"], | ||||
|           null | undefined | ||||
|         >["data"][number]["attributes"], | ||||
|         null | undefined | ||||
|       >["contents"] | ||||
| ) { | ||||
|   contents?.data.sort((a, b) => { | ||||
|     if ( | ||||
|       a.attributes.range[0].__typename === "ComponentRangePageRange" && | ||||
|       b.attributes.range[0].__typename === "ComponentRangePageRange" | ||||
|       a.attributes?.range[0]?.__typename === "ComponentRangePageRange" && | ||||
|       b.attributes?.range[0]?.__typename === "ComponentRangePageRange" | ||||
|     ) { | ||||
|       return ( | ||||
|         a.attributes.range[0].starting_page - | ||||
| @ -255,8 +360,8 @@ export function sortContent(contents: { | ||||
| 
 | ||||
| export function getStatusDescription( | ||||
|   status: string, | ||||
|   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"] | ||||
| ): string { | ||||
|   langui: AppStaticProps["langui"] | ||||
| ): string | null | undefined { | ||||
|   switch (status) { | ||||
|     case Enum_Componentsetstextset_Status.Incomplete: | ||||
|       return langui.status_incomplete; | ||||
| @ -300,15 +405,15 @@ export function randomInt(min: number, max: number) { | ||||
| } | ||||
| 
 | ||||
| export function getLocalesFromLanguages( | ||||
|   languages: { | ||||
|     language: { | ||||
|       data: { | ||||
|         attributes: { | ||||
|           code: string; | ||||
|         }; | ||||
|       }; | ||||
|     }; | ||||
|   }[] | ||||
|   languages?: Array<{ | ||||
|     language?: { | ||||
|       data?: { | ||||
|         attributes?: { code: string } | null; | ||||
|       } | null; | ||||
|     } | null; | ||||
|   } | null> | null | ||||
| ) { | ||||
|   return languages.map((language) => language.language.data.attributes.code); | ||||
|   return languages | ||||
|     ? languages.map((language) => language?.language?.data?.attributes?.code) | ||||
|     : []; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint