├── src ├── styles │ ├── components │ │ ├── _index.scss │ │ └── _links.scss │ ├── abstracts │ │ ├── _index.scss │ │ ├── variables │ │ │ ├── _index.scss │ │ │ ├── _misc.scss │ │ │ ├── _typography.scss │ │ │ └── _themes.scss │ │ └── _mixins.scss │ ├── main.scss │ ├── modules │ │ ├── components │ │ │ ├── name │ │ │ │ ├── did-you-know.module.scss │ │ │ │ ├── info.module.scss │ │ │ │ ├── reviews.module.scss │ │ │ │ ├── known-for.module.scss │ │ │ │ ├── credits.module.scss │ │ │ │ └── basic.module.scss │ │ │ ├── title │ │ │ │ ├── did-you-know.module.scss │ │ │ │ ├── reviews.module.scss │ │ │ │ ├── more-like-this.module.scss │ │ │ │ ├── cast.module.scss │ │ │ │ ├── info.module.scss │ │ │ │ └── basic.module.scss │ │ │ ├── card │ │ │ │ ├── card.module.scss │ │ │ │ ├── card-cast.module.scss │ │ │ │ ├── card-result.module.scss │ │ │ │ ├── card-title.module.scss │ │ │ │ └── card-basic.module.scss │ │ │ ├── find │ │ │ │ ├── person.module.scss │ │ │ │ ├── results.module.scss │ │ │ │ └── title.module.scss │ │ │ ├── buttons │ │ │ │ └── themeToggler.module.scss │ │ │ ├── list │ │ │ │ ├── pagination.module.scss │ │ │ │ ├── meta.module.scss │ │ │ │ ├── images.module.scss │ │ │ │ ├── names.module.scss │ │ │ │ └── titles.module.scss │ │ │ ├── loaders │ │ │ │ └── progress-bar.module.scss │ │ │ ├── error │ │ │ │ └── error-info.module.scss │ │ │ ├── media │ │ │ │ └── media.module.scss │ │ │ └── form │ │ │ │ └── find.module.scss │ │ ├── pages │ │ │ ├── list │ │ │ │ └── list.module.scss │ │ │ ├── contact │ │ │ │ └── contact.module.scss │ │ │ ├── privacy │ │ │ │ └── privacy.module.scss │ │ │ ├── find │ │ │ │ └── find.module.scss │ │ │ ├── name │ │ │ │ └── name.module.scss │ │ │ ├── title │ │ │ │ └── title.module.scss │ │ │ └── about │ │ │ │ └── about.module.scss │ │ └── layout │ │ │ ├── footer.module.scss │ │ │ └── header.module.scss │ └── base │ │ ├── _index.scss │ │ ├── _fonts.scss │ │ ├── _typography.scss │ │ ├── _helpers.scss │ │ ├── _root.scss │ │ ├── _base.scss │ │ └── _reset.scss ├── components │ ├── list │ │ ├── index.ts │ │ ├── OptionalLink.tsx │ │ ├── Images.tsx │ │ ├── Data.tsx │ │ ├── Meta.tsx │ │ ├── Pagination.tsx │ │ ├── Names.tsx │ │ └── Titles.tsx │ ├── loaders │ │ └── ProgressBar.tsx │ ├── card │ │ ├── index.tsx │ │ ├── Card.tsx │ │ ├── CardResult.tsx │ │ ├── CardBasic.tsx │ │ ├── CardCast.tsx │ │ └── CardTitle.tsx │ ├── name │ │ ├── index.ts │ │ ├── Bio.tsx │ │ ├── KnownFor.tsx │ │ ├── Credits.tsx │ │ ├── DidYouKnow.tsx │ │ ├── Basic.tsx │ │ └── Info.tsx │ ├── title │ │ ├── index.ts │ │ ├── Cast.tsx │ │ ├── MoreLikeThis.tsx │ │ ├── Reviews.tsx │ │ ├── DidYouKnow.tsx │ │ └── Basic.tsx │ ├── find │ │ ├── Keyword.tsx │ │ ├── Company.tsx │ │ ├── Person.tsx │ │ ├── Title.tsx │ │ └── index.tsx │ ├── layout │ │ ├── index.tsx │ │ ├── Footer.tsx │ │ └── Header.tsx │ ├── buttons │ │ └── ThemeToggler.tsx │ ├── error │ │ ├── ErrorBoundary.tsx │ │ └── ErrorInfo.tsx │ ├── meta │ │ └── Meta.tsx │ ├── media │ │ └── Media.tsx │ └── forms │ │ └── find │ │ └── index.tsx ├── interfaces │ ├── shared │ │ ├── error.ts │ │ ├── index.ts │ │ ├── name.ts │ │ ├── title.ts │ │ ├── list.ts │ │ └── search.ts │ └── misc │ │ └── rawFind.ts ├── pages │ ├── 404.tsx │ ├── 500.tsx │ ├── api │ │ ├── [...error].ts │ │ ├── name │ │ │ └── [nameId].ts │ │ ├── title │ │ │ └── [titleId].ts │ │ ├── list │ │ │ └── [listId].ts │ │ ├── find.ts │ │ └── media_proxy.ts │ ├── _app.tsx │ ├── [...error] │ │ └── index.tsx │ ├── _document.tsx │ ├── list │ │ └── [listId] │ │ │ └── index.tsx │ ├── contact │ │ └── index.tsx │ ├── name │ │ └── [nameId] │ │ │ └── index.tsx │ ├── find │ │ └── index.tsx │ ├── privacy │ │ └── index.tsx │ └── title │ │ └── [titleId] │ │ └── index.tsx ├── utils │ ├── constants │ │ ├── keys.ts │ │ └── find.ts │ ├── axiosInstance.ts │ ├── redis.ts │ ├── getOrSetApiCache.ts │ ├── fetchers │ │ ├── basicSearch.ts │ │ ├── name.ts │ │ ├── title.ts │ │ └── list.ts │ ├── cleaners │ │ └── find.ts │ └── helpers.ts ├── hooks │ └── usePageLoading.ts └── context │ └── theme-context.tsx ├── public ├── robots.txt ├── favicon.ico ├── icon-192.png ├── icon-512.png ├── apple-touch-icon.png ├── img │ └── misc │ │ ├── preview.jpg │ │ └── preview2.jpg ├── fonts │ ├── RedHatDisplay-VariableFont_wght.ttf │ └── RedHatDisplay-Italic-VariableFont_wght.ttf ├── site.webmanifest ├── icon.svg └── svg │ └── sprite.svg ├── .eslintrc.json ├── .prettierrc ├── .gitignore ├── next.config.mjs ├── .versionrc ├── tsconfig.json ├── package.json ├── docker-compose.example.yml ├── Dockerfile ├── .env.local.example └── CHANGELOG.md /src/styles/components/_index.scss: -------------------------------------------------------------------------------- 1 | @forward './links'; -------------------------------------------------------------------------------- /src/styles/abstracts/_index.scss: -------------------------------------------------------------------------------- 1 | @forward './mixins'; 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: /about 3 | Disallow: / 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/favicon.ico -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @use './base'; 4 | @use './components'; 5 | -------------------------------------------------------------------------------- /public/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/icon-192.png -------------------------------------------------------------------------------- /public/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/icon-512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/misc/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/img/misc/preview.jpg -------------------------------------------------------------------------------- /public/img/misc/preview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/img/misc/preview2.jpg -------------------------------------------------------------------------------- /src/styles/abstracts/variables/_index.scss: -------------------------------------------------------------------------------- 1 | @forward './misc'; 2 | @forward './typography'; 3 | @forward './themes'; 4 | -------------------------------------------------------------------------------- /src/styles/modules/components/name/did-you-know.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | gap: var(--comp-whitespace); 4 | } 5 | -------------------------------------------------------------------------------- /public/fonts/RedHatDisplay-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/fonts/RedHatDisplay-VariableFont_wght.ttf -------------------------------------------------------------------------------- /src/styles/components/_links.scss: -------------------------------------------------------------------------------- 1 | @use '../abstracts' as helper; 2 | 3 | .link, 4 | .ipc-md-link { 5 | @include helper.prettify-link(var(--clr-link)); 6 | } 7 | -------------------------------------------------------------------------------- /public/fonts/RedHatDisplay-Italic-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDevel/libremdb/main/public/fonts/RedHatDisplay-Italic-VariableFont_wght.ttf -------------------------------------------------------------------------------- /src/components/list/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Data } from './Data'; 2 | export { default as Meta } from './Meta'; 3 | export { default as Pagination } from './Pagination'; 4 | -------------------------------------------------------------------------------- /src/styles/base/_index.scss: -------------------------------------------------------------------------------- 1 | @forward './reset'; 2 | @forward './helpers'; 3 | @forward './root'; 4 | @forward './base'; 5 | @forward './fonts'; 6 | @forward './typography'; 7 | -------------------------------------------------------------------------------- /src/interfaces/shared/error.ts: -------------------------------------------------------------------------------- 1 | import { AppError as AppErrorClass } from 'src/utils/helpers'; 2 | 3 | export type AppError = Omit, 'name'>; 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "react/no-unescaped-entities": "off", 5 | "@next/next/no-page-custom-font": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/styles/base/_fonts.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'RedHat Display'; 3 | src: url('../../../public/fonts/RedHatDisplay-VariableFont_wght.ttf'); 4 | font-display: swap; 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "arrowParens": "avoid", 5 | "semi": true, 6 | "singleQuote": true, 7 | "jsxSingleQuote": true, 8 | "printWidth": 100 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import ErrorInfo from 'src/components/error/ErrorInfo'; 2 | 3 | const Error404 = () => { 4 | return ; 5 | }; 6 | 7 | export default Error404; 8 | -------------------------------------------------------------------------------- /src/pages/500.tsx: -------------------------------------------------------------------------------- 1 | import ErrorInfo from 'src/components/error/ErrorInfo'; 2 | 3 | const Error500 = () => { 4 | return ; 5 | }; 6 | export default Error500; 7 | -------------------------------------------------------------------------------- /src/styles/modules/components/title/did-you-know.module.scss: -------------------------------------------------------------------------------- 1 | .didYouKnow { 2 | display: grid; 3 | gap: var(--comp-whitespace); 4 | } 5 | 6 | .container { 7 | display: grid; 8 | gap: var(--comp-whitespace); 9 | } 10 | -------------------------------------------------------------------------------- /src/components/loaders/ProgressBar.tsx: -------------------------------------------------------------------------------- 1 | import styles from 'src/styles/modules/components/loaders/progress-bar.module.scss'; 2 | 3 | const ProgressBar = () => { 4 | return ; 5 | }; 6 | export default ProgressBar; 7 | -------------------------------------------------------------------------------- /src/components/card/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Card } from './Card'; 2 | export { default as CardTitle } from './CardTitle'; 3 | export { default as CardBasic } from './CardBasic'; 4 | export { default as CardCast } from './CardCast'; 5 | export { default as CardResult } from './CardResult'; 6 | -------------------------------------------------------------------------------- /src/interfaces/shared/index.ts: -------------------------------------------------------------------------------- 1 | import type Name from './name'; 2 | 3 | export type Media = Name['media']; // exactly the same in title and name 4 | 5 | // forcefully makes array of individual elements of T, where t is any conditional type. 6 | export type ToArray = T extends any ? T[] : never; 7 | -------------------------------------------------------------------------------- /src/styles/modules/components/card/card.module.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | overflow: hidden; 3 | border-radius: 5px; 4 | background-color: var(--clr-bg-accent); 5 | box-shadow: var(--clr-shadow); 6 | } 7 | 8 | .hoverable:hover, 9 | .hoverable:focus-within { 10 | background-color: var(--clr-bg-muted); 11 | } 12 | -------------------------------------------------------------------------------- /src/components/name/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Basic } from './Basic'; 2 | export { default as DidYouKnow } from './DidYouKnow'; 3 | export { default as Info } from './Info'; 4 | export { default as Credits } from './Credits'; 5 | export { default as KnownFor } from './KnownFor'; 6 | export { default as Bio } from './Bio'; 7 | -------------------------------------------------------------------------------- /src/pages/api/[...error].ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from 'next'; 2 | 3 | type ResponseData = { status: false; message: string }; 4 | 5 | export default async function handler(_: NextApiRequest, res: NextApiResponse) { 6 | res.status(400).json({ status: false, message: 'Not found' }); 7 | } 8 | -------------------------------------------------------------------------------- /src/components/title/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Basic } from './Basic'; 2 | export { default as Cast } from './Cast'; 3 | export { default as DidYouKnow } from './DidYouKnow'; 4 | export { default as Info } from './Info'; 5 | export { default as MoreLikeThis } from './MoreLikeThis'; 6 | export { default as Reviews } from './Reviews'; 7 | -------------------------------------------------------------------------------- /src/styles/modules/components/find/person.module.scss: -------------------------------------------------------------------------------- 1 | .basicInfo { 2 | display: flex; 3 | list-style: none; 4 | flex-wrap: wrap; 5 | 6 | & * + ::before { 7 | content: '\00b7'; 8 | padding-inline: var(--spacer-1); 9 | font-weight: 900; 10 | line-height: 0; 11 | font-size: var(--fs-5); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/styles/modules/components/buttons/themeToggler.module.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | border: none; 3 | background: none; 4 | cursor: pointer; 5 | } 6 | 7 | .icon { 8 | // we'll get --dimension var from header.module.scss 9 | height: var(--dimension, 4rem); 10 | width: var(--dimension, 4rem); 11 | 12 | fill: var(--clr-fill); 13 | } 14 | -------------------------------------------------------------------------------- /src/styles/modules/components/list/pagination.module.scss: -------------------------------------------------------------------------------- 1 | .nav { 2 | display: flex; 3 | list-style: none; 4 | justify-content: center; 5 | flex-wrap: wrap; 6 | align-items: center; 7 | 8 | gap: var(--spacer-3); 9 | 10 | [aria-hidden="true"] { 11 | cursor: not-allowed; 12 | } 13 | 14 | li { 15 | text-align: center; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/utils/constants/keys.ts: -------------------------------------------------------------------------------- 1 | export const titleKey = (titleId: string) => `title:${titleId}`; 2 | export const nameKey = (nameId: string) => `name:${nameId}`; 3 | export const listKey = (listId: string, pageNum = '1') => `list:${listId}?page=${pageNum}`; 4 | export const findKey = (query: string) => `find:${query}`; 5 | export const mediaKey = (url: string) => `media:${url}`; 6 | -------------------------------------------------------------------------------- /src/components/name/Bio.tsx: -------------------------------------------------------------------------------- 1 | import styles from 'src/styles/modules/components/name/did-you-know.module.scss'; 2 | 3 | type Props = { bio: string }; 4 | 5 | const Bio = ({ bio }: Props) => ( 6 |
7 |

About

8 |
9 |
10 | ); 11 | 12 | export default Bio; 13 | -------------------------------------------------------------------------------- /src/styles/modules/components/list/meta.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: grid; 3 | gap: var(--spacer-1); 4 | } 5 | 6 | .list { 7 | list-style: none; 8 | display: flex; 9 | flex-wrap: wrap; 10 | 11 | & * + *::before { 12 | content: '\00b7'; 13 | padding-inline: var(--spacer-0); 14 | font-weight: 900; 15 | line-height: 0; 16 | font-size: var(--fs-5); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/find/Keyword.tsx: -------------------------------------------------------------------------------- 1 | import { CardResult } from 'src/components/card'; 2 | import { Keywords } from 'src/interfaces/shared/search'; 3 | 4 | type Props = { keyword: Keywords[number] }; 5 | 6 | const Keyword = ({ keyword }: Props) => ( 7 | 8 | {keyword.numTitles &&

{keyword.numTitles} titles

} 9 |
10 | ); 11 | 12 | export default Keyword; 13 | -------------------------------------------------------------------------------- /src/styles/modules/components/name/info.module.scss: -------------------------------------------------------------------------------- 1 | .info { 2 | display: grid; 3 | 4 | gap: var(--doc-whitespace); 5 | } 6 | 7 | .accolades, .details { 8 | display: grid; 9 | gap: var(--comp-whitespace); 10 | 11 | &__container { 12 | display: grid; 13 | gap: var(--spacer-0); 14 | 15 | // for span elements like these: 'release date:' 16 | & > p > span:first-of-type { 17 | font-weight: var(--fw-bold); 18 | } 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/interfaces/shared/name.ts: -------------------------------------------------------------------------------- 1 | import cleanName from 'src/utils/cleaners/name'; 2 | 3 | type Name = ReturnType; 4 | export type { Name as default }; 5 | 6 | export type Basic = Name['basic']; 7 | 8 | export type Media = Name['media']; 9 | 10 | export type Credits = Name['credits']; 11 | 12 | export type DidYouKnow = Name['didYouKnow']; 13 | 14 | export type PersonalDetails = Name['personalDetails']; 15 | 16 | export type KnownFor = Name['knownFor']; 17 | -------------------------------------------------------------------------------- /src/styles/modules/pages/list/list.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../abstracts' as helper; 2 | 3 | .list { 4 | --doc-whitespace: var(--spacer-8); 5 | --comp-whitespace: var(--spacer-3); 6 | 7 | display: grid; 8 | 9 | gap: var(--doc-whitespace); 10 | padding: var(--doc-whitespace); 11 | 12 | @include helper.bp('bp-700') { 13 | --doc-whitespace: var(--spacer-5); 14 | } 15 | 16 | @include helper.bp('bp-450') { 17 | padding: var(--spacer-3); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/styles/base/_typography.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: var(--ff-base); 3 | font-size: var(--fs-5); 4 | } 5 | 6 | .heading { 7 | color: var(--clr-text-accent); 8 | font-family: var(--ff-accent); 9 | font-weight: var(--fw-medium); 10 | line-height: 1.2; 11 | 12 | &__primary { 13 | font-size: var(--fs-1); 14 | } 15 | 16 | &__secondary { 17 | font-size: var(--fs-2); 18 | } 19 | 20 | &__tertiary { 21 | font-size: var(--fs-3); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libremdb", 3 | "short_name": "libremdb", 4 | "icons": [ 5 | { 6 | "src": "/icon-192.png", 7 | "sizes": "192x192", 8 | "type": "image/png", 9 | "purpose": "any maskable" 10 | }, 11 | { 12 | "src": "/icon-512.png", 13 | "sizes": "512x512", 14 | "type": "image/png", 15 | "purpose": "any maskable" 16 | } 17 | ], 18 | "theme_color": "#b80040", 19 | "background_color": "#ffe5ef" 20 | } 21 | -------------------------------------------------------------------------------- /src/styles/modules/components/find/results.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../abstracts' as helper; 2 | 3 | .results { 4 | display: grid; 5 | gap: var(--spacer-2); 6 | 7 | &__list { 8 | display: grid; 9 | gap: var(--spacer-5); 10 | } 11 | } 12 | 13 | .titles, 14 | .people, 15 | .companies, 16 | .keywords { 17 | display: grid; 18 | gap: var(--spacer-2); 19 | 20 | &__list { 21 | padding: var(--spacer-2); 22 | display: grid; 23 | gap: var(--spacer-4); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/find/Company.tsx: -------------------------------------------------------------------------------- 1 | import { CardResult } from 'src/components/card'; 2 | import { Companies } from 'src/interfaces/shared/search'; 3 | 4 | type Props = { company: Companies[number] }; 5 | 6 | const Company = ({ company }: Props) => ( 7 | 8 | {company.country &&

{company.country}

} 9 | {!!company.type &&

{company.type}

} 10 |
11 | ); 12 | 13 | export default Company; 14 | -------------------------------------------------------------------------------- /src/styles/modules/components/find/title.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../abstracts' as helper; 2 | 3 | 4 | .basicInfo, 5 | .seriesInfo { 6 | display: flex; 7 | list-style: none; 8 | flex-wrap: wrap; 9 | 10 | & :not(:last-child)::after { 11 | content: '\00b7'; 12 | padding-inline: var(--spacer-1); 13 | font-weight: 900; 14 | line-height: 0; 15 | font-size: var(--fs-5); 16 | } 17 | } 18 | 19 | .stars { 20 | span { 21 | font-weight: var(--fw-bold); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/list/OptionalLink.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactNode, ComponentPropsWithoutRef } from 'react'; 2 | import Link from 'next/link'; 3 | 4 | const OptionalLink = ({ 5 | href, 6 | children, 7 | ...rest 8 | }: { href?: string | null; children: ReactNode } & Omit, 'href'>) => ( 9 | <> 10 | {href ? ( 11 | 12 | {children} 13 | 14 | ) : ( 15 | children 16 | )} 17 | 18 | ); 19 | 20 | export default OptionalLink; 21 | -------------------------------------------------------------------------------- /src/styles/modules/components/name/reviews.module.scss: -------------------------------------------------------------------------------- 1 | .reviews { 2 | display: grid; 3 | gap: var(--comp-whitespace); 4 | 5 | &__reviewContainer { 6 | // background-color: antiquewhite; 7 | } 8 | 9 | &__stats { 10 | display: flex; 11 | flex-wrap: wrap; 12 | gap: var(--spacer-2); 13 | } 14 | } 15 | 16 | .review { 17 | &__summary { 18 | font-size: calc(var(--fs-5) * 1.1); 19 | cursor: pointer; 20 | } 21 | 22 | &__text, 23 | &__metadata { 24 | padding-top: var(--spacer-2); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/styles/modules/components/title/reviews.module.scss: -------------------------------------------------------------------------------- 1 | .reviews { 2 | display: grid; 3 | gap: var(--comp-whitespace); 4 | 5 | &__reviewContainer { 6 | // background-color: antiquewhite; 7 | } 8 | 9 | &__stats { 10 | display: flex; 11 | flex-wrap: wrap; 12 | gap: var(--spacer-2); 13 | } 14 | } 15 | 16 | .review { 17 | &__summary { 18 | font-size: calc(var(--fs-5) * 1.1); 19 | cursor: pointer; 20 | } 21 | 22 | &__text, 23 | &__metadata { 24 | padding-top: var(--spacer-2); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/utils/axiosInstance.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const axiosInstance = axios.create({ 4 | baseURL: 'https://www.imdb.com/', 5 | timeout: 50000, 6 | headers: { 7 | ...(process.env.AXIOS_USERAGENT && { 8 | 'User-Agent': process.env.AXIOS_USERAGENT, 9 | }), 10 | ...(process.env.AXIOS_ACCEPT && { Accept: process.env.AXIOS_ACCEPT }), 11 | ...(process.env.AXIOS_LANGUAGE && { 12 | 'Accept-Language': process.env.AXIOS_LANGUAGE, 13 | }), 14 | }, 15 | }); 16 | 17 | export default axiosInstance; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # next.js 10 | /.next/ 11 | /out/ 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .pnpm-debug.log* 25 | 26 | # local env files 27 | .env*.local 28 | 29 | # typescript 30 | *.tsbuildinfo 31 | next-env.d.ts 32 | 33 | #just dev stuff 34 | dev/* 35 | 36 | # other lockfiles 37 | yarn.lock 38 | package-lock.json 39 | 40 | # docker 41 | docker-compose.yml 42 | dump.rdb -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | async redirects() { 6 | return [ 7 | { 8 | source: '/', 9 | destination: '/find', 10 | permanent: true, 11 | }, 12 | ]; 13 | }, 14 | images: { 15 | domains: ['m.media-amazon.com'], 16 | }, 17 | experimental: { 18 | images: { 19 | allowFutureImage: true, 20 | }, 21 | isrMemoryCacheSize: 20 * 1024 * 1024, 22 | }, 23 | poweredByHeader: false, 24 | }; 25 | 26 | export default nextConfig; 27 | -------------------------------------------------------------------------------- /src/utils/redis.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import Redis from 'ioredis'; 3 | 4 | const redisUrl = process.env.REDIS_URL; 5 | const toUseRedis = 6 | process.env.USE_REDIS === 'true' || process.env.USE_REDIS_FOR_API_ONLY === 'true'; 7 | 8 | const stub: Pick = { 9 | get: async key => Promise.resolve(null), 10 | setex: async (key, seconds, value) => Promise.resolve('OK'), 11 | getBuffer: (key, callback) => Promise.resolve(null), 12 | }; 13 | 14 | const redis = toUseRedis && redisUrl ? new Redis(redisUrl) : stub; 15 | 16 | export default redis; 17 | -------------------------------------------------------------------------------- /src/styles/modules/components/loaders/progress-bar.module.scss: -------------------------------------------------------------------------------- 1 | .progress { 2 | position: fixed; 3 | z-index: 1; 4 | inset-inline: 0; 5 | inset-block-start: 0; 6 | height: 4px; 7 | width: 100%; 8 | background: var(--clr-fill); 9 | transform: translateX(-100%); 10 | box-shadow: 2px 0 5px var(--clr-fill); 11 | border-top-right-radius: 5px; 12 | border-bottom-right-radius: 5px; 13 | 14 | animation: prograte 60s ease-out forwards; 15 | } 16 | 17 | @keyframes prograte { 18 | 5% { 19 | transform: translateX(-40%); 20 | } 21 | 100% { 22 | transform: translateX(-3%); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/layout/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import Footer from './Footer'; 3 | import Header from './Header'; 4 | 5 | type Props = { 6 | full?: true; 7 | children: ReactNode; 8 | className: string; 9 | originalPath?: string; 10 | }; 11 | 12 | const Layout = ({ full, children, className, originalPath }: Props) => { 13 | return ( 14 | <> 15 |
16 |
17 | {children} 18 |
19 |