diff --git a/src/modules/api/Comment.js b/src/modules/api/Comment.js new file mode 100644 index 0000000..d7dd186 --- /dev/null +++ b/src/modules/api/Comment.js @@ -0,0 +1,54 @@ +import axios from 'axios'; + +export default class Comments { + constructor() { + this.involvementApi = process.env.INVOLVEMENT_API_LINK; + this.involvementApiId = process.env.INVOLVEMENT_ID; + this.commentsEndPoint = `/apps/${this.involvementApiId}/comments`; + this.commentsAllEndPoint = `/apps/${this.involvementApiId}/comments?item_id=`; + } + /** + * call to Involvement APi for getting comments + * @param id + * @returns api data + */ + + // eslint-disable-next-line consistent-return + getComments = async (id) => { + try { + // eslint-disable-next-line consistent-return + return await axios.get(`${this.involvementApi}${this.commentsAllEndPoint}${id}`).then((res) => { + if (res.status === 200) { + return res; + } + }); + } catch (error) { + if (error.response) { + return false; + } + } + } + + /** + * call to Involvement APi for adding new + * @param itemId, username, comment + * @returns api data + */ + addComment = async (itemId, username, comment) => { + try { + // eslint-disable-next-line consistent-return + return await axios.post(`${this.involvementApi}${this.commentsEndPoint}`, { + item_id: itemId, + username, + comment, + }).then((res) => { + if (res.status === 201) { + return true; + } + throw new Error('Fail'); + }); + } catch (error) { + throw new Error('Fail'); + } + } +} \ No newline at end of file diff --git a/src/modules/api/ShowApi.js b/src/modules/api/ShowApi.js index e57589b..4564f69 100644 --- a/src/modules/api/ShowApi.js +++ b/src/modules/api/ShowApi.js @@ -1,17 +1,23 @@ import axios from 'axios'; // eslint-disable-next-line import/no-cycle, import/no-unresolved import { navigator } from '../router/router.js'; +// eslint-disable-next-line import/no-cycle +import Comments from './Comment.js'; -export default class ShowApi { +export default class ShowApi extends Comments { constructor() { + super(); this.movieApi = process.env.MOVIEDB_API_LINK; this.movieApiSecret = process.env.MOVIEDB_API_SECRET; - this.involvementApi = process.env.INVOLVEMENT_API_LINK; - this.involvementApiId = process.env.INVOLVEMENT_ID; this.apiSecretCall = `?api_key=${this.movieApiSecret}&language=en-US`; this.noMovieMsg = 'Request failed with status code 404'; } + /** + * call to movie db for getting people + * @param {id} id + * @returns api data + */ people = async (id) => { try { return await axios.get(`${this.movieApi}/${id}/credits${this.apiSecretCall}`).then((res) => (res.data)); @@ -25,6 +31,11 @@ export default class ShowApi { } } + /** + * call to movie db for getting recommended movie + * @param {id} id + * @returns api data + */ recommendations = async (id) => { try { return await axios.get(`${this.movieApi}/${id}/recommendations${this.apiSecretCall}`).then((res) => (res.data)); @@ -33,6 +44,11 @@ export default class ShowApi { } } + /** + * call to movie db for getting the movie by id + * @param {id} id + * @returns api data + */ show = async (id) => { try { // eslint-disable-next-line consistent-return @@ -43,8 +59,6 @@ export default class ShowApi { }); } catch (error) { if (error.message === this.noMovieMsg) { - // window.history.pushState({ path: '404' }, '404', 'error'); - // navigator('/404'); throw new Error('No such page'); } throw new Error('No such page'); diff --git a/src/modules/main.js b/src/modules/main.js index 75215c3..af225a1 100644 --- a/src/modules/main.js +++ b/src/modules/main.js @@ -12,4 +12,7 @@ export default class Main { links(); }); }; + + js = async () => { + } } \ No newline at end of file diff --git a/src/modules/router/router.js b/src/modules/router/router.js index b6a6bf5..0254f2b 100644 --- a/src/modules/router/router.js +++ b/src/modules/router/router.js @@ -25,12 +25,13 @@ const scrollToTop = () => { set to el.innerHtml */ const newPage = async (Page, el, obj = {}) => { - // intiial image should be null + // initial image should be null const images = document.querySelectorAll('.image'); images.forEach((image) => { image.innerHTML = ''; }); imgHelper(); const page = new Page(); el.innerHTML = await page.html(obj); + await page.js(); links(); scrollToTop(); }; @@ -54,8 +55,6 @@ const noPage = (el) => { // for main navigation const navigator = (path) => { - // get main - const main = document.querySelector('.main'); let route = path; let hash; if (route.includes('#')) { @@ -64,19 +63,15 @@ const navigator = (path) => { } const routeInfo = routerInstance.routes.filter((rou) => (rou.path === route || `${rou.path}/` === route))[0]; if (!routeInfo || path === '404') { - noPage(main); // eslint-disable-next-line no-use-before-define router(); } else if (routeInfo.params && hash) { - newPage(routeInfo.page, main, { hash: Number(window.location.hash) }); // eslint-disable-next-line no-use-before-define router(); } else if (!routeInfo.params) { - newPage(routeInfo.page, main, {}); // eslint-disable-next-line no-use-before-define router(); } else { - noPage(main); // eslint-disable-next-line no-use-before-define router(); } @@ -85,7 +80,7 @@ const navigator = (path) => { /** * navigate according to button */ -const navigate = (event) => { +const navigate = async (event) => { event.preventDefault(); let route = event.target.attributes[0].value; let hash; diff --git a/src/modules/show.js b/src/modules/show.js index 2fb08d8..6604d1d 100644 --- a/src/modules/show.js +++ b/src/modules/show.js @@ -6,8 +6,14 @@ export default class Show extends ShowApi { super(); this.color = ['blue', 'green', 'red', 'aqua', 'lightblue']; this.photo = 'https://image.tmdb.org/t/p/original'; + this.itemNotFound = "item_id' not found."; } + /** + * change to smaller string for the given length text + * @param num + * @returns string + */ toText = (num) => { let string = num; for (let i = 3; i < num.toString().length; i += 4) { @@ -16,6 +22,11 @@ export default class Show extends ShowApi { return string; } + /** + * get in string format for innerHtml adding: get show more show less button + * @param string + * @returns string + */ showMore = (str) => { let string = str; if (str.length > 100) { @@ -31,6 +42,11 @@ export default class Show extends ShowApi { return string; } + /** + * get in string format for innerHtml adding: adding genre + * @param Array + * @returns string + */ toGenre = (genres) => { if (genres.length > 0) { return Array.from(genres, (e) => `${e.name}`).join(''); @@ -38,6 +54,11 @@ export default class Show extends ShowApi { return 'N/A'; } + /** + * get in string format for innerHtml adding: adding people + * @param Array + * @returns string + */ toPeople = (p) => { let string = ''; for (let i = 0; i < 5 && i < p.length; i += 1) { @@ -46,14 +67,24 @@ export default class Show extends ShowApi { return string; } + /** + * get in string format for innerHtml adding: adding recommendations cards + * @param Array + * @returns string + */ toRecommendations = (data) => { let string = ''; for (let i = 0; i < 5 && i < data.length; i += 1) { - string += `
${this.checkNull(data[i].poster_path, `photo`)}
${data[i].title}
`; + string += `
${this.checkNull(data[i].poster_path, `photo`)}
${(data[i].title.length > 20) ? `${data[i].title.slice(0, 20)}...` : data[i].title}
`; } return string; } + /** + * check null or not if null return 'N/A' otherwise text + * @param (data: noyNullStr, text: Str) + * @returns string + */ checkNull = (data, text) => { if (!data) { return 'N/A'; @@ -61,6 +92,72 @@ export default class Show extends ShowApi { return text; } + /** + * change to the data objects + * @param str + * @returns string + */ + toDate = (str) => { + const date = new Date(str); + return `${date.getDate()}/${date.getMonth()}/${date.getFullYear()}`; + } + + /** + * get in string format for innerHtml adding: adding comments + * @param (obj: comments) + * @returns string + */ + toComments = (data) => { + let string = ''; + data.forEach((comment) => { + string += `
+ ${this.toDate(comment.creation_date)} + ${comment.username} + ${comment.comment} +
`; + }); + return string; + } + + /** + * get the comments length that is in dom + * @param + * @returns comments length + */ + commentsLength = () => { + const commentsLength = document.querySelector('#comments-length'); + const comments = document.querySelectorAll('.comment'); + commentsLength.innerHTML = comments.length; + return comments.length; + } + /** + * to run the javascript code after the html from the class is finished + * @param () + */ + + js = () => { + const form = document.querySelector('.form'); + + form.addEventListener('submit', async (e) => { + e.preventDefault(); + const commentsEl = document.querySelector('.comments'); + this.addComment(e.target.id, e.target[0].value, e.target[1].value).then(async (res) => { + if (res === true) { + const comments = await this.getComments(e.target.id); + commentsEl.innerHTML = this.toComments(comments.data); + this.commentsLength(); + e.target.reset(); + } + }); + }); + this.commentsLength(); + } + + /** + * for main.innerHtml data + * @param () + * @return (main.innerHtml: string) + */ html = async ({ hash }) => { const res = await this.show(Number(hash)).then((data) => data); const { @@ -68,16 +165,19 @@ export default class Show extends ShowApi { } = res; const posterPath = res.poster_path; const rating = res.vote_average; - const releaseDate = res.release_date; + const releaseDate = this.toDate(res.release_date); const language = res.original_language; const people = await this.people(id).then((data) => data); const recommendations = await this.recommendations(id).then((data) => data); + const comments = await this.getComments(id); return (`
@@ -105,6 +205,18 @@ export default class Show extends ShowApi {
${this.toRecommendations(recommendations.results)}
+ +

Comments()

+
+ ${(comments === false) ? 'No comments yet' : this.toComments(comments.data)} +
+ +

Add a comment

+
+ + + +
` diff --git a/src/style.css b/src/style.css index eb3d031..8114584 100644 --- a/src/style.css +++ b/src/style.css @@ -247,6 +247,36 @@ footer { flex-wrap: wrap; } +.comment-form { + display: grid; + grid-template-columns: 100%; + gap: 20px; +} + +.input-box { + padding: 5px; +} + +.input-name { + width: 375px; +} + +.input-btn { + padding: 10px; + width: 100px; +} + +.comments { + display: grid; + grid-template-columns: 1fr; + gap: 15px; +} + +.comment { + display: flex; + gap: 10px; +} + .spaLink { cursor: pointer; }