feat(route): add name route
adds much needed route fix https://github.com/zyachel/libremdb/issues/39, https://github.com/zyachel/libremdb/issues/36, https://codeberg.org/zyachel/libremdb/issues/11
This commit is contained in:
57
src/components/name/Basic.tsx
Normal file
57
src/components/name/Basic.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { CardBasic } from 'src/components/card';
|
||||
import { Basic as BasicType } from 'src/interfaces/shared/name';
|
||||
import { formatNumber } from 'src/utils/helpers';
|
||||
import styles from 'src/styles/modules/components/name/basic.module.scss';
|
||||
|
||||
type Props = {
|
||||
className: string;
|
||||
data: BasicType;
|
||||
};
|
||||
|
||||
const Basic = ({ data, className }: Props) => {
|
||||
return (
|
||||
<CardBasic className={className} image={data.poster.url} title={data.name}>
|
||||
<div className={styles.ratings}>
|
||||
{data.ranking && (
|
||||
<p className={styles.rating}>
|
||||
<span className={styles.rating__num}>{formatNumber(data.ranking.position)}</span>
|
||||
<svg className={styles.rating__icon}>
|
||||
<use href='/svg/sprite.svg#icon-graph-rising'></use>
|
||||
</svg>
|
||||
<span className={styles.rating__text}>
|
||||
{' '}
|
||||
Popularity (
|
||||
<span className={styles.rating__sub}>{getRankingStats(data.ranking)}</span>)
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!!data.primaryProfessions.length && (
|
||||
<p className={styles.genres}>
|
||||
<span className={styles.heading}>Profession: </span>
|
||||
{data.primaryProfessions.join(', ')}
|
||||
</p>
|
||||
)}
|
||||
{
|
||||
<p className={styles.overview}>
|
||||
<span className={styles.heading}>About: </span>
|
||||
{data.bio.short}...
|
||||
</p>
|
||||
}
|
||||
<p className={styles.genres}>
|
||||
<span className={styles.heading}>Known for: </span>
|
||||
{data.knownFor.title} ({data.knownFor.role})
|
||||
</p>
|
||||
</CardBasic>
|
||||
);
|
||||
};
|
||||
|
||||
const getRankingStats = (ranking: NonNullable<Props['data']['ranking']>) => {
|
||||
if (ranking.direction === 'FLAT') return '\u2192';
|
||||
|
||||
const change = formatNumber(ranking.change);
|
||||
return (ranking.direction === 'UP' ? '\u2191' : '\u2193') + change;
|
||||
};
|
||||
|
||||
export default Basic;
|
12
src/components/name/Bio.tsx
Normal file
12
src/components/name/Bio.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import styles from 'src/styles/modules/components/name/did-you-know.module.scss';
|
||||
|
||||
type Props = { bio: string };
|
||||
|
||||
const Bio = ({ bio }: Props) => (
|
||||
<section className={styles.bio}>
|
||||
<h2 className='heading heading__secondary'>About</h2>
|
||||
<div dangerouslySetInnerHTML={{ __html: bio }} />
|
||||
</section>
|
||||
);
|
||||
|
||||
export default Bio;
|
73
src/components/name/Credits.tsx
Normal file
73
src/components/name/Credits.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { Credits } from 'src/interfaces/shared/name';
|
||||
import { CardTitle } from 'src/components/card';
|
||||
import styles from 'src/styles/modules/components/name/credits.module.scss';
|
||||
|
||||
type Props = {
|
||||
className: string;
|
||||
data: Credits;
|
||||
};
|
||||
|
||||
const Credits = ({ className, data }: Props) => {
|
||||
if (!data.total) return null;
|
||||
|
||||
return (
|
||||
<section className={`${className} ${styles.credits}`}>
|
||||
<h2 className='heading heading__secondary'>Credits</h2>
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Released</h3>
|
||||
{data.released.map(
|
||||
(item, i) =>
|
||||
!!item.total && (
|
||||
<details open={i === 0} key={item.category.id}>
|
||||
<summary>
|
||||
{item.category.text} ({item.total})
|
||||
</summary>
|
||||
<ul className={styles.container} key={item.category.id}>
|
||||
{item.titles.map(title => (
|
||||
<CardTitle
|
||||
key={title.id}
|
||||
link={`/title/${title.id}`}
|
||||
name={title.title}
|
||||
titleType={title.type.text}
|
||||
image={title.poster?.url}
|
||||
year={title.releaseYear}
|
||||
ratings={title.ratings}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
)
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Unreleased</h3>
|
||||
{data.unreleased.map(
|
||||
(item, i) =>
|
||||
!!item.total && (
|
||||
<details open={i === 0} key={item.category.id}>
|
||||
<summary>
|
||||
{item.category.text} ({item.total})
|
||||
</summary>
|
||||
<ul className={styles.container}>
|
||||
{item.titles.map(title => (
|
||||
<CardTitle
|
||||
key={title.id}
|
||||
link={`/title/${title.id}`}
|
||||
name={title.title}
|
||||
titleType={title.type.text}
|
||||
image={title.poster?.url}
|
||||
year={title.releaseYear}
|
||||
>
|
||||
<p>{title.productionStatus}</p>
|
||||
</CardTitle>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
)
|
||||
)}
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Credits;
|
53
src/components/name/DidYouKnow.tsx
Normal file
53
src/components/name/DidYouKnow.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import Link from 'next/link';
|
||||
import { DidYouKnow } from 'src/interfaces/shared/name';
|
||||
import styles from 'src/styles/modules/components/name/did-you-know.module.scss';
|
||||
|
||||
type Props = {
|
||||
data: DidYouKnow;
|
||||
};
|
||||
|
||||
const DidYouKnow = ({ data }: Props) => (
|
||||
<section className={styles.didYouKnow}>
|
||||
<h2 className='heading heading__secondary'>Did you know</h2>
|
||||
<div className={styles.container}>
|
||||
{!!data.trivia?.total && (
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Trivia</h3>
|
||||
<div dangerouslySetInnerHTML={{ __html: data.trivia.html }}></div>
|
||||
</section>
|
||||
)}
|
||||
{!!data.quotes?.total && (
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Quotes</h3>
|
||||
<div dangerouslySetInnerHTML={{ __html: data.quotes.html }}></div>
|
||||
</section>
|
||||
)}
|
||||
{!!data.trademark?.total && (
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Trademark</h3>
|
||||
<div dangerouslySetInnerHTML={{ __html: data.trademark.html }}></div>
|
||||
</section>
|
||||
)}
|
||||
{!!data.nicknames.length && (
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Nicknames</h3>
|
||||
<p>{data.nicknames.join(', ')}</p>
|
||||
</section>
|
||||
)}
|
||||
{!!data.salary?.total && (
|
||||
<section>
|
||||
<h3 className='heading heading__tertiary'>Salary</h3>
|
||||
<p>
|
||||
<span>{data.salary.value} in </span>
|
||||
<Link href={`/title/${data.salary.title.id}`}>
|
||||
<a className={'link'}>{data.salary.title.text}</a>
|
||||
</Link>
|
||||
<span> ({data.salary.title.year})</span>
|
||||
</p>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
export default DidYouKnow;
|
192
src/components/name/Info.tsx
Normal file
192
src/components/name/Info.tsx
Normal file
@ -0,0 +1,192 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import Name, { PersonalDetails } from 'src/interfaces/shared/name';
|
||||
import styles from 'src/styles/modules/components/name/info.module.scss';
|
||||
|
||||
type Props = {
|
||||
info: PersonalDetails;
|
||||
accolades: Name['accolades'];
|
||||
};
|
||||
|
||||
const PersonalDetails = ({ info, accolades }: Props) => {
|
||||
const {
|
||||
query: { nameId },
|
||||
} = useRouter();
|
||||
|
||||
return (
|
||||
<div className={styles.info}>
|
||||
<section className={styles.accolades}>
|
||||
<h2 className='heading heading__secondary'>Accolades</h2>
|
||||
<div className={styles.accolades__container}>
|
||||
{accolades.awards && (
|
||||
<p>
|
||||
<span>
|
||||
Won {accolades.awards.wins} {accolades.awards.name}
|
||||
</span>
|
||||
<span> (out of {accolades.awards.nominations} nominations)</span>
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
{accolades.wins} wins and {accolades.nominations} nominations in total
|
||||
</p>
|
||||
<p>
|
||||
<Link href={`/name/${nameId}/awards`}>
|
||||
<a className='link'>View all awards</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.details}>
|
||||
<h2 className='heading heading__secondary'>Personal details</h2>
|
||||
<div className={styles.details__container}>
|
||||
{!!info.officialSites.length && (
|
||||
<p>
|
||||
<span>Official sites: </span>
|
||||
{info.officialSites.map((site, i) => (
|
||||
<span key={site.url}>
|
||||
{!!i && ', '}
|
||||
<a href={site.url} className='link' target='_blank' rel='noreferrer'>
|
||||
{site.name}
|
||||
</a>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{!!info.alsoKnownAs.length && (
|
||||
<p>
|
||||
<span>Also known as: </span>
|
||||
<span>{info.alsoKnownAs.join(', ')}</span>
|
||||
</p>
|
||||
)}
|
||||
{info.height && (
|
||||
<p>
|
||||
<span>Height: </span>
|
||||
<span>{info.height}</span>
|
||||
</p>
|
||||
)}
|
||||
{info.birth && (
|
||||
<p>
|
||||
<span>Born: </span>
|
||||
<span>{info.birth.date}</span>
|
||||
<span>
|
||||
{' '}
|
||||
in{' '}
|
||||
<Link href={`/search/name?birth_place=${info.birth.location}`}>
|
||||
<a className='link'>{info.birth.location}</a>
|
||||
</Link>
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{info.death.date && (
|
||||
<p>
|
||||
<span>Died: </span>
|
||||
<span>{info.death.date}</span>
|
||||
<span>
|
||||
{' '}
|
||||
in{' '}
|
||||
<Link href={`/search/name?death_place=${info.death.location}`}>
|
||||
<a className='link'>{info.death.location}</a>
|
||||
</Link>
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{info.death.cause && (
|
||||
<p>
|
||||
<span>Death cause: </span>
|
||||
<span>{info.death.cause}</span>
|
||||
</p>
|
||||
)}
|
||||
{!!info.spouses?.length && (
|
||||
<p>
|
||||
<span>Spouses: </span>
|
||||
{info.spouses.map((spouse, i) => (
|
||||
<span key={spouse.name}>
|
||||
<>
|
||||
{!!i && ', '}
|
||||
{renderPersonNameWithLink(spouse)} {spouse.range} (
|
||||
{spouse.attributes.join(', ')})
|
||||
</>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{!!info.children?.length && (
|
||||
<p>
|
||||
<span>Children: </span>
|
||||
{info.children.map((child, i) => (
|
||||
<span key={child.name}>
|
||||
<>
|
||||
{!!i && ', '}
|
||||
{renderPersonNameWithLink(child)}
|
||||
</>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{!!info.parents?.length && (
|
||||
<p>
|
||||
<span>Parents: </span>
|
||||
{info.parents.map((parent, i) => (
|
||||
<span key={parent.name}>
|
||||
<>
|
||||
{!!i && ', '}
|
||||
{renderPersonNameWithLink(parent)}
|
||||
</>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{!!info.relatives?.length && (
|
||||
<p>
|
||||
<span>Relatives: </span>
|
||||
{info.relatives.map((relative, i) => (
|
||||
<span key={relative.name}>
|
||||
<>
|
||||
{!!i && ', '}
|
||||
{renderPersonNameWithLink(relative)} ({relative.relation})
|
||||
</>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{!!info.otherWorks?.length && (
|
||||
<p>
|
||||
<span>Other Works: </span>
|
||||
{info.otherWorks.map((work, i) => (
|
||||
<span key={work.text}>
|
||||
<>
|
||||
{!!i && ', '}
|
||||
<span dangerouslySetInnerHTML={{ __html: work.text }} />
|
||||
</>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{!!info.publicity.total && (
|
||||
<p>
|
||||
<span>Publicity Listings: </span>
|
||||
<span>{info.publicity.articles} Articles</span>,{' '}
|
||||
<span>{info.publicity.interviews} Interviews</span>,{' '}
|
||||
<span>{info.publicity.magazines} Magazines</span>,{' '}
|
||||
<span>{info.publicity.pictorials} Pictorials</span>,{' '}
|
||||
<span>{info.publicity.printBiographies} Print biographies</span>, and{' '}
|
||||
<span>{info.publicity.filmBiographies} Biographies</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalDetails;
|
||||
|
||||
const renderPersonNameWithLink = (person: { name: string; id: string | null }) =>
|
||||
person.id ? (
|
||||
<Link href={`/name/${person.id}`}>
|
||||
<a className='link'>{person.name}</a>
|
||||
</Link>
|
||||
) : (
|
||||
<span>{person.name}</span>
|
||||
);
|
34
src/components/name/KnownFor.tsx
Normal file
34
src/components/name/KnownFor.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import type { KnownFor as KnownForType } from 'src/interfaces/shared/name';
|
||||
import { CardTitle } from 'src/components/card';
|
||||
import styles from 'src/styles/modules/components/name/known-for.module.scss';
|
||||
|
||||
type Props = { data: KnownForType };
|
||||
|
||||
const KnownFor = ({ data }: Props) => {
|
||||
if (!data.length) return null;
|
||||
|
||||
return (
|
||||
<section className={styles.knownFor}>
|
||||
<h2 className='heading heading__secondary'>Known For</h2>
|
||||
<ul className={styles.container}>
|
||||
{data.map(title => (
|
||||
<CardTitle
|
||||
key={title.id}
|
||||
link={`/title/${title.id}`}
|
||||
name={title.title}
|
||||
titleType={title.type.text}
|
||||
image={title.poster?.url}
|
||||
year={title.releaseYear}
|
||||
>
|
||||
<p className={styles.item__role}>{getRoles(title)}</p>
|
||||
</CardTitle>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const getRoles = (title: Props['data'][number]) =>
|
||||
(title.summary.characters ?? title.summary.jobs)?.join(', ');
|
||||
|
||||
export default KnownFor;
|
6
src/components/name/index.tsx
Normal file
6
src/components/name/index.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as Basic } from './Basic';
|
||||
export { default as DidYouKnow } from './DidYouKnow';
|
||||
export { default as Info } from './Info';
|
||||
export { default as Credits } from './Credits';
|
||||
export { default as KnownFor } from './KnownFor';
|
||||
export { default as Bio } from './Bio';
|
Reference in New Issue
Block a user