Added responsive design classes, moved some things to common folder, added loading states
This commit is contained in:
parent
f1b3d9e5be
commit
81e0e88eaa
|
@ -1,6 +1,5 @@
|
|||
import { ReactEventHandler, useState } from "react";
|
||||
|
||||
export default ({ pages, onPageChange, currentPage }: any) => {
|
||||
export default function Pagination({ pages, onPageChange, currentPage }: any) {
|
||||
const [page, setPage] = useState(currentPage);
|
||||
const handleClick = (e: any) => {
|
||||
const { value } = e.target;
|
||||
|
@ -62,4 +61,4 @@ export default ({ pages, onPageChange, currentPage }: any) => {
|
|||
<QuickLink value={">>"} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import "@testing-library/jest-dom";
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import MovieCard from '../src/app/Search/MovieCard.tsx'
|
||||
import MovieCard from '../src/common/components/MovieCard.tsx'
|
||||
const movie = {
|
||||
Title: "Dune",
|
||||
Year: "2000",
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
import "@testing-library/jest-dom";
|
||||
import { queryOMDb } from "../src/api/omdb.ts";
|
||||
|
||||
test("queryOMDb returns movie data", async () => {
|
||||
test("queryOMDb returns movie data successfully", async () => {
|
||||
const data = await queryOMDb("s=back to");
|
||||
|
||||
expect(data).toHaveProperty("Response");
|
||||
expect(data).toHaveProperty("Response", "True");
|
||||
});
|
||||
|
||||
test("queryOMDb returns movie with error", async () => {
|
||||
const data = await queryOMDb("f=dune");
|
||||
|
||||
expect(data).toHaveProperty("Response", "False");
|
||||
});
|
||||
|
||||
// f=dune
|
||||
|
||||
// Warning: React does not recognize the `fetchPriority` prop on a DOM element
|
||||
// The `punycode` module is deprecated. Please use a userland alternative instead.
|
288
movie-search/package-lock.json
generated
288
movie-search/package-lock.json
generated
|
@ -10,18 +10,19 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"framer-motion": "^11.2.6",
|
||||
"next": "14.2.3",
|
||||
"next": "^14.2.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"ts-node-dev": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.24.6",
|
||||
"@testing-library/jest-dom": "^6.4.5",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.3",
|
||||
|
@ -157,6 +158,18 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-annotate-as-pure": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz",
|
||||
"integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz",
|
||||
|
@ -191,6 +204,38 @@
|
|||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.6.tgz",
|
||||
"integrity": "sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.24.6",
|
||||
"@babel/helper-environment-visitor": "^7.24.6",
|
||||
"@babel/helper-function-name": "^7.24.6",
|
||||
"@babel/helper-member-expression-to-functions": "^7.24.6",
|
||||
"@babel/helper-optimise-call-expression": "^7.24.6",
|
||||
"@babel/helper-replace-supers": "^7.24.6",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.24.6",
|
||||
"@babel/helper-split-export-declaration": "^7.24.6",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz",
|
||||
|
@ -225,6 +270,18 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-member-expression-to-functions": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.6.tgz",
|
||||
"integrity": "sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz",
|
||||
|
@ -255,6 +312,18 @@
|
|||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-optimise-call-expression": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.6.tgz",
|
||||
"integrity": "sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz",
|
||||
|
@ -264,6 +333,23 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-replace-supers": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.6.tgz",
|
||||
"integrity": "sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.24.6",
|
||||
"@babel/helper-member-expression-to-functions": "^7.24.6",
|
||||
"@babel/helper-optimise-call-expression": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-simple-access": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz",
|
||||
|
@ -276,6 +362,18 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.6.tgz",
|
||||
"integrity": "sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz",
|
||||
|
@ -593,6 +691,60 @@
|
|||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-modules-commonjs": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.6.tgz",
|
||||
"integrity": "sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.24.6",
|
||||
"@babel/helper-plugin-utils": "^7.24.6",
|
||||
"@babel/helper-simple-access": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-typescript": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.6.tgz",
|
||||
"integrity": "sha512-H0i+hDLmaYYSt6KU9cZE0gb3Cbssa/oxWis7PX4ofQzbvsfix9Lbh8SRk7LCPDlLWJHUiFeHU0qRRpF/4Zv7mQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.24.6",
|
||||
"@babel/helper-create-class-features-plugin": "^7.24.6",
|
||||
"@babel/helper-plugin-utils": "^7.24.6",
|
||||
"@babel/plugin-syntax-typescript": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-typescript": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.6.tgz",
|
||||
"integrity": "sha512-U10aHPDnokCFRXgyT/MaIRTivUu2K/mu0vJlwRS9LxJmJet+PFQNKpggPyFCUtC6zWSBPjvxjnpNkAn3Uw2m5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.24.6",
|
||||
"@babel/helper-validator-option": "^7.24.6",
|
||||
"@babel/plugin-syntax-jsx": "^7.24.6",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.24.6",
|
||||
"@babel/plugin-transform-typescript": "^7.24.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
|
||||
|
@ -1930,14 +2082,12 @@
|
|||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz",
|
||||
"integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==",
|
||||
"dev": true,
|
||||
"version": "18.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
|
||||
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
|
@ -1947,7 +2097,6 @@
|
|||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
|
||||
"integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
|
@ -9473,6 +9622,15 @@
|
|||
"jsesc": "^2.5.1"
|
||||
}
|
||||
},
|
||||
"@babel/helper-annotate-as-pure": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz",
|
||||
"integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-compilation-targets": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz",
|
||||
|
@ -9503,6 +9661,31 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.6.tgz",
|
||||
"integrity": "sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.24.6",
|
||||
"@babel/helper-environment-visitor": "^7.24.6",
|
||||
"@babel/helper-function-name": "^7.24.6",
|
||||
"@babel/helper-member-expression-to-functions": "^7.24.6",
|
||||
"@babel/helper-optimise-call-expression": "^7.24.6",
|
||||
"@babel/helper-replace-supers": "^7.24.6",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.24.6",
|
||||
"@babel/helper-split-export-declaration": "^7.24.6",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz",
|
||||
|
@ -9528,6 +9711,15 @@
|
|||
"@babel/types": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-member-expression-to-functions": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.6.tgz",
|
||||
"integrity": "sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-imports": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz",
|
||||
|
@ -9549,12 +9741,32 @@
|
|||
"@babel/helper-validator-identifier": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-optimise-call-expression": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.6.tgz",
|
||||
"integrity": "sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-plugin-utils": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz",
|
||||
"integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-replace-supers": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.6.tgz",
|
||||
"integrity": "sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-environment-visitor": "^7.24.6",
|
||||
"@babel/helper-member-expression-to-functions": "^7.24.6",
|
||||
"@babel/helper-optimise-call-expression": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-simple-access": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz",
|
||||
|
@ -9564,6 +9776,15 @@
|
|||
"@babel/types": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-skip-transparent-expression-wrappers": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.6.tgz",
|
||||
"integrity": "sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz",
|
||||
|
@ -9793,6 +10014,42 @@
|
|||
"@babel/helper-plugin-utils": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-modules-commonjs": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.6.tgz",
|
||||
"integrity": "sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-module-transforms": "^7.24.6",
|
||||
"@babel/helper-plugin-utils": "^7.24.6",
|
||||
"@babel/helper-simple-access": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-typescript": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.6.tgz",
|
||||
"integrity": "sha512-H0i+hDLmaYYSt6KU9cZE0gb3Cbssa/oxWis7PX4ofQzbvsfix9Lbh8SRk7LCPDlLWJHUiFeHU0qRRpF/4Zv7mQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.24.6",
|
||||
"@babel/helper-create-class-features-plugin": "^7.24.6",
|
||||
"@babel/helper-plugin-utils": "^7.24.6",
|
||||
"@babel/plugin-syntax-typescript": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/preset-typescript": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.6.tgz",
|
||||
"integrity": "sha512-U10aHPDnokCFRXgyT/MaIRTivUu2K/mu0vJlwRS9LxJmJet+PFQNKpggPyFCUtC6zWSBPjvxjnpNkAn3Uw2m5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.24.6",
|
||||
"@babel/helper-validator-option": "^7.24.6",
|
||||
"@babel/plugin-syntax-jsx": "^7.24.6",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.24.6",
|
||||
"@babel/plugin-transform-typescript": "^7.24.6"
|
||||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
|
||||
|
@ -10815,14 +11072,12 @@
|
|||
"@types/prop-types": {
|
||||
"version": "15.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "18.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz",
|
||||
"integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==",
|
||||
"dev": true,
|
||||
"version": "18.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
|
||||
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
|
@ -10832,7 +11087,6 @@
|
|||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
|
||||
"integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
|
|
|
@ -13,18 +13,19 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"framer-motion": "^11.2.6",
|
||||
"next": "14.2.3",
|
||||
"next": "^14.2.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"ts-node-dev": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.24.6",
|
||||
"@testing-library/jest-dom": "^6.4.5",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.3",
|
||||
|
|
|
@ -3,6 +3,8 @@ const url = `${process.env.OMDB_URL}?apikey=${process.env.API_KEY}`;
|
|||
export const queryOMDb = async (queryParamString: string) => {
|
||||
"use server";
|
||||
try {
|
||||
// await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
const queryUrl = `${url}&${queryParamString}`;
|
||||
const response = await fetch(queryUrl);
|
||||
if (!response.ok) {
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
'use client'
|
||||
"use client";
|
||||
|
||||
import { FavoriteContext } from "@/components/FavoriteContext";
|
||||
import { FavoriteContext } from "@/common/components/FavoriteContext";
|
||||
import MovieList from "@/common/components/MovieList";
|
||||
import { useContext } from "react";
|
||||
|
||||
export default () => {
|
||||
const {favorites} = useContext(FavoriteContext)
|
||||
export default function FavoriteList() {
|
||||
const { favorites } = useContext(FavoriteContext);
|
||||
return (
|
||||
<ul>
|
||||
{favorites.map((f, i) => (
|
||||
<li key={i}>{f.Title}</li>
|
||||
))}
|
||||
</ul>
|
||||
<MovieList movies={favorites} />
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import FavoriteList from "./favoriteList";
|
||||
|
||||
export default () => {
|
||||
export default function Favorites() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Your Favorites!</h1>
|
||||
|
|
5
movie-search/src/app/Movie/loading.tsx
Normal file
5
movie-search/src/app/Movie/loading.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Loading from "@/common/components/Loading";
|
||||
|
||||
export default function loading() {
|
||||
return <Loading />;
|
||||
}
|
|
@ -1,21 +1,22 @@
|
|||
import { queryOMDb } from "@/api/omdb";
|
||||
import { INextJsProps, stringifyQueryParams } from "../Search/page";
|
||||
import ImgCard from "@/components/ImgCard";
|
||||
import { INextJsProps } from "../Search/page";
|
||||
import ImgCard from "@/common/components/ImgCard";
|
||||
import { stringifyQueryParams } from "@/common/utils/searchParams";
|
||||
|
||||
export default async ({ searchParams }: INextJsProps) => {
|
||||
export default async function Movie({ searchParams }: INextJsProps) {
|
||||
const movie = await queryOMDb(stringifyQueryParams(searchParams!));
|
||||
const { Poster, Ratings, ...rest } = movie;
|
||||
const { Poster, Title, Ratings, ...details } = movie;
|
||||
return (
|
||||
<div className="rounded p-4 bg-gray-300 w-auto mx-4 flex-column">
|
||||
<div className="rounded p-4 bg-gray-300 w-auto mx-4 flex-column h-xl">
|
||||
<ImgCard
|
||||
width={400}
|
||||
height={580}
|
||||
src={movie.Poster !== "N/A" ? movie.Poster : ""}
|
||||
alt={`${movie.Title} Movie Poster`}
|
||||
src={Poster !== "N/A" ? Poster : ""}
|
||||
alt={`${details.Title} Movie Poster`}
|
||||
>
|
||||
<div className="mt-auto">
|
||||
<h3 className="mb-2 text-lg font-semibold">{movie.Title}</h3>
|
||||
{Object.entries(rest).map(([key, value]) => {
|
||||
<h3 className="mb-2 text-lg font-semibold">{Title}</h3>
|
||||
{Object.entries(details).map(([key, value]) => {
|
||||
return (
|
||||
<p key={key} className="mb-2 text-sm text-gray-700">
|
||||
<strong>{key}: </strong>
|
||||
|
@ -25,7 +26,7 @@ export default async ({ searchParams }: INextJsProps) => {
|
|||
})}
|
||||
<h3>Ratings:</h3>
|
||||
<ul>
|
||||
{movie.Ratings.map((r: { Source: string; Value: string }) => (
|
||||
{Ratings.map((r: { Source: string; Value: string }) => (
|
||||
<li key={r.Source}>
|
||||
{r.Source} - {r.Value}
|
||||
</li>
|
||||
|
@ -35,4 +36,4 @@ export default async ({ searchParams }: INextJsProps) => {
|
|||
</ImgCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import FavoriteBtn from "@/components/FavoriteBtn";
|
||||
import ImgCard from "@/components/ImgCard";
|
||||
import Link from "next/link";
|
||||
|
||||
export interface IMovieCardItem {
|
||||
Title: string;
|
||||
Year: string;
|
||||
imdbID: string;
|
||||
Type: string;
|
||||
Poster: string;
|
||||
}
|
||||
interface IMovieCardProps {
|
||||
movie: IMovieCardItem;
|
||||
}
|
||||
|
||||
export default ({ movie }: IMovieCardProps) => {
|
||||
return (
|
||||
<ImgCard
|
||||
classNames={`w-48`}
|
||||
width={140}
|
||||
height={160}
|
||||
src={movie.Poster !== "N/A" ? movie.Poster : ""}
|
||||
alt={`${movie.Title} Movie Poster`}
|
||||
>
|
||||
<div className="mt-auto">
|
||||
<h3 className="mb-2 text-lg font-semibold">{movie.Title}</h3>
|
||||
<p className="mb-2 text-sm text-gray-700">Circa: {movie.Year}</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Link
|
||||
href={`/Movie?i=${movie.imdbID}`}
|
||||
className="block rounded-lg bg-purple-500 px-4 py-2 text-center font-semibold text-white hover:bg-purple-600"
|
||||
>
|
||||
Movie Details
|
||||
</Link>
|
||||
<FavoriteBtn movie={movie} />
|
||||
</div>
|
||||
</ImgCard>
|
||||
);
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
import MovieCard, { IMovieCardItem } from "./MovieCard";
|
||||
|
||||
interface IMovieListProps {
|
||||
movies: Array<IMovieCardItem>;
|
||||
}
|
||||
|
||||
export default ({ movies }: IMovieListProps) => {
|
||||
return (
|
||||
<div className="container m-auto">
|
||||
<div className={`p-4 flex justify-start flex-wrap gap-4`}>
|
||||
{movies.map((m) => (
|
||||
<div className="WrapItem" key={m.imdbID}>
|
||||
<MovieCard movie={m} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,20 +1,24 @@
|
|||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default () => {
|
||||
export default function SearchBar() {
|
||||
const searchMovies = async (formData: FormData) => {
|
||||
"use server";
|
||||
|
||||
const movieTitle = formData.get("movieTitle");
|
||||
// revalidatePath('/Search')
|
||||
redirect(`/Search?s=${movieTitle}`);
|
||||
};
|
||||
return (
|
||||
<form action={searchMovies} className={`flex gap-4 m-4 justify-center`}>
|
||||
<input
|
||||
className="rounded px-2"
|
||||
placeholder="Search by Title"
|
||||
name="movieTitle"
|
||||
/>
|
||||
<div className="flex gap-4">
|
||||
<form action={searchMovies} className={`md:flex gap-4 m-4 justify-center`}>
|
||||
<div className="flex justify-center">
|
||||
<input
|
||||
className="rounded px-2 mb-2 md:mb-0"
|
||||
placeholder="Search by Title"
|
||||
name="movieTitle"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 justify-center">
|
||||
<button
|
||||
type="submit"
|
||||
className={`rounded px-2 bg-purple-500 hover:bg-purple-700`}
|
||||
|
@ -30,4 +34,4 @@ export default () => {
|
|||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
16
movie-search/src/app/Search/SearchPage.tsx
Normal file
16
movie-search/src/app/Search/SearchPage.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { queryOMDb } from "@/api/omdb";
|
||||
import MovieList from "../../common/components/MovieList";
|
||||
|
||||
export default async function SearchPage({
|
||||
queryParamString,
|
||||
}: {
|
||||
queryParamString: string;
|
||||
}) {
|
||||
|
||||
const { Search } = await queryOMDb(queryParamString);
|
||||
const movies = Search || [];
|
||||
|
||||
return (
|
||||
<MovieList movies={movies} />
|
||||
);
|
||||
}
|
|
@ -1,29 +1,22 @@
|
|||
import { queryOMDb } from "@/api/omdb";
|
||||
import MovieList from "./MovieList";
|
||||
import { stringifyQueryParams } from "@/common/utils/searchParams";
|
||||
import { Suspense } from "react";
|
||||
import SearchPage from "./SearchPage";
|
||||
import SearchBar from "./SearchBar";
|
||||
import Loading from "@/common/components/Loading";
|
||||
|
||||
export interface INextJsProps {
|
||||
searchParams?: object;
|
||||
}
|
||||
|
||||
export const stringifyQueryParams = (searchParams: object) =>
|
||||
Object.entries(searchParams)
|
||||
.map(
|
||||
([key, value], i, arr) =>
|
||||
`${key}=${value}${i < arr.length - 1 ? "&" : ""}`
|
||||
)
|
||||
.toString();
|
||||
|
||||
export default async ({ searchParams }: INextJsProps) => {
|
||||
export default async function Search({ searchParams }: INextJsProps) {
|
||||
const queryParamString = stringifyQueryParams(searchParams!);
|
||||
// console.log(queryParamString)
|
||||
const { Search } = await queryOMDb(queryParamString);
|
||||
const movies = Search || [];
|
||||
|
||||
return (
|
||||
<div className={`mx-auto px-2`}>
|
||||
<SearchBar />
|
||||
<MovieList movies={movies} />
|
||||
<Suspense key={queryParamString} fallback={<Loading />}>
|
||||
<SearchPage queryParamString={queryParamString} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -51,12 +51,12 @@
|
|||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
|
||||
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
||||
--primary-glow: radial-gradient(rgba(210, 1, 255, 0.5), rgba(1, 65, 255, 0));
|
||||
--secondary-glow: linear-gradient(
|
||||
to bottom right,
|
||||
rgba(1, 65, 255, 0),
|
||||
rgba(1, 65, 255, 0),
|
||||
rgba(1, 65, 255, 0.3)
|
||||
rgba(180, 1, 255, 0.3)
|
||||
);
|
||||
|
||||
--tile-start-rgb: 2, 13, 46;
|
||||
|
@ -92,6 +92,7 @@ body {
|
|||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
/* background: #2bff0110 */
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import NavBar from "@/components/navBar";
|
||||
import { FavoriteProvider } from "@/components/FavoriteContext";
|
||||
import NavBar from "@/common/components/NavBar";
|
||||
import { FavoriteProvider } from "@/common/components/FavoriteContext";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
title: "Movie Search App",
|
||||
description: "Powered by Nextjs and OMDb.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
@ -17,8 +17,8 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${inter.className} mb-4`}>
|
||||
<html lang="en" className="h-full">
|
||||
<body className={`${inter.className} mb-4 bg-black h-full`}>
|
||||
<NavBar />
|
||||
<FavoriteProvider>
|
||||
{children}
|
||||
|
|
1
movie-search/src/common/classes.ts
Normal file
1
movie-search/src/common/classes.ts
Normal file
|
@ -0,0 +1 @@
|
|||
const responsiveContainer = ''
|
29
movie-search/src/common/components/FavoriteBtn.tsx
Normal file
29
movie-search/src/common/components/FavoriteBtn.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
|
||||
import { useContext } from "react";
|
||||
import { FavoriteContext } from "./FavoriteContext";
|
||||
|
||||
export default function FavoriteBtn({ movie }: any) {
|
||||
const { favorites, setFavorites } = useContext(FavoriteContext);
|
||||
const filteredFavorites = favorites.filter((f) => f.imdbID != movie.imdbID);
|
||||
const isFavorite =
|
||||
favorites.filter((f) => f.imdbID == movie.imdbID).length == 1;
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (isFavorite) {
|
||||
setFavorites([...filteredFavorites]);
|
||||
} else {
|
||||
setFavorites([...favorites, movie]);
|
||||
}
|
||||
}}
|
||||
className={`block text-yellow-500 rounded my-auto h-8 w-8
|
||||
${
|
||||
isFavorite ? " bg-black" : "border-2 border-gray-300 bg-white"
|
||||
}`}
|
||||
>
|
||||
★
|
||||
</button>
|
||||
);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
import { IMovieCardItem } from "@/app/Search/MovieCard";
|
||||
|
||||
import { PropsWithChildren, createContext, useState } from "react";
|
||||
import { IMovieCardItem } from "./MovieCard";
|
||||
|
||||
interface IFavoriteContext {
|
||||
favorites: Array<IMovieCardItem>
|
|
@ -7,19 +7,18 @@ interface ICardProps extends PropsWithChildren {
|
|||
alt: string;
|
||||
classNames?: string;
|
||||
}
|
||||
export default ({
|
||||
export default function ImgCard({
|
||||
classNames,
|
||||
width,
|
||||
height,
|
||||
src,
|
||||
alt,
|
||||
children,
|
||||
}: ICardProps) => {
|
||||
}: ICardProps) {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
shadow-md
|
||||
shadow
|
||||
shadow-lg
|
||||
shadow-purple-500
|
||||
rounded
|
||||
|
9
movie-search/src/common/components/Loading.tsx
Normal file
9
movie-search/src/common/components/Loading.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default function Loading() {
|
||||
return (
|
||||
<div className="h-svh flex flex-col justify-center">
|
||||
<h1 className="my-auto text-center">
|
||||
<strong>Loading...</strong>
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
41
movie-search/src/common/components/MovieCard.tsx
Normal file
41
movie-search/src/common/components/MovieCard.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import FavoriteBtn from "@/common/components/FavoriteBtn";
|
||||
import ImgCard from "@/common/components/ImgCard";
|
||||
import Link from "next/link";
|
||||
|
||||
export interface IMovieCardItem {
|
||||
Title: string;
|
||||
Year: string;
|
||||
imdbID: string;
|
||||
Type: string;
|
||||
Poster: string;
|
||||
}
|
||||
interface IMovieCardProps {
|
||||
movie: IMovieCardItem;
|
||||
}
|
||||
|
||||
export default function MovieCard({ movie }: IMovieCardProps) {
|
||||
return (
|
||||
<ImgCard
|
||||
classNames={`w-full h-full flex flex-col justify-between`}
|
||||
width={140}
|
||||
height={160}
|
||||
src={movie.Poster !== "N/A" ? movie.Poster : ""}
|
||||
alt={`${movie.Title} Movie Poster`}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h3 className=" text-lg font-semibold">{movie.Title}</h3>
|
||||
<p className=" text-sm text-gray-700">Circa: {movie.Year}</p>
|
||||
|
||||
<div className="flex gap-3 justify-around">
|
||||
<Link
|
||||
href={`/Movie?i=${movie.imdbID}`}
|
||||
className="block rounded-lg bg-purple-500 px-4 py-2 text-center font-semibold text-white hover:bg-purple-600"
|
||||
>
|
||||
Movie Details
|
||||
</Link>
|
||||
<FavoriteBtn movie={movie} />
|
||||
</div>
|
||||
</div>
|
||||
</ImgCard>
|
||||
);
|
||||
}
|
25
movie-search/src/common/components/MovieList.tsx
Normal file
25
movie-search/src/common/components/MovieList.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import MovieCard, { IMovieCardItem } from "./MovieCard";
|
||||
|
||||
export interface IMovieListProps {
|
||||
movies: Array<IMovieCardItem>;
|
||||
}
|
||||
|
||||
export default function MovieList({ movies }: IMovieListProps) {
|
||||
return (
|
||||
<div className="container m-auto">
|
||||
<div
|
||||
className={`p-1 flex justify-around flex-wrap gap-4 sm:justify-center `}
|
||||
>
|
||||
{movies.length > 0 ? (
|
||||
movies.map((m) => (
|
||||
<div className="WrapItem w-full mx-5 sm:w-64 mx-1" key={m.imdbID}>
|
||||
<MovieCard movie={m} />
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<h1>Nothing to show</h1>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import Link from "next/link";
|
||||
|
||||
export default () => {
|
||||
export default function NavBar() {
|
||||
return (
|
||||
<nav className="px-2 flex gap-4">
|
||||
<nav className="px-2 flex gap-4 shadow-md shadow-purple-500 mt-2 mb-4">
|
||||
<Link href="/">
|
||||
<strong>Home</strong>
|
||||
</Link>
|
||||
|
@ -10,4 +10,4 @@ export default () => {
|
|||
<Link href="/Favorites">Favorites</Link>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
}
|
7
movie-search/src/common/utils/searchParams.ts
Normal file
7
movie-search/src/common/utils/searchParams.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export const stringifyQueryParams = (searchParams: object) =>
|
||||
Object.entries(searchParams)
|
||||
.map(
|
||||
([key, value], i, arr) =>
|
||||
`${key}=${value}${i < arr.length - 1 ? "&" : ""}`
|
||||
)
|
||||
.toString();
|
|
@ -1,25 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { useContext } from "react";
|
||||
import { FavoriteContext } from "./FavoriteContext";
|
||||
|
||||
export default ({ movie }: any) => {
|
||||
const { favorites, setFavorites } = useContext(FavoriteContext);
|
||||
const filteredFavorites = favorites.filter(f => f.imdbID != movie.imdbID)
|
||||
const isFavorite = favorites.filter(f => f.imdbID == movie.imdbID).length == 1
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (isFavorite) {
|
||||
setFavorites([...filteredFavorites])
|
||||
} else {
|
||||
setFavorites([...favorites, movie]);
|
||||
}
|
||||
}}
|
||||
className={`block text-yellow-500 rounded my-auto w-6 ${isFavorite ? 'bg-black' : 'bg-white'}`}
|
||||
>
|
||||
★
|
||||
</button>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user