diff --git a/src/app/components/GroupCardsLearningObjects.js b/src/app/components/GroupCardsLearningObjects.js index 7d370a202419064b6450890eb756efdb58bfbdde..7921f30df36963d051a8a0b4d359dfe840c91aa7 100644 --- a/src/app/components/GroupCardsLearningObjects.js +++ b/src/app/components/GroupCardsLearningObjects.js @@ -1,5 +1,5 @@ "use client"; -import { useEffect, useLayoutEffect, useState } from "react"; +import { useCallback, useEffect, useLayoutEffect, useState } from "react"; import Cards from "./Cards"; import mecredApi from "@/axiosConfig"; import { Button, Divider } from "@mui/material"; @@ -11,26 +11,20 @@ export default function GroupCardsLearningObjects({title, cardsPerRow, url}) { const [learningObjects, setLearningObjects] = useState([]); useEffect(() => { + const handleResize = (length) => { + if (length > cardsPerRow) { + setShowButton(true); + } + }; + const fetchlearningObjects = async (url) => { + const { data } = await mecredApi.get(url); + setLearningObjects(data); + handleResize(data.length); + }; if (cardsPerRow > 0) { fetchlearningObjects(url); } - }, [cardsPerRow]); - - const handleResize = (length) => { - if (length > cardsPerRow) { - setShowButton(true); - } - }; - - const fetchlearningObjects = async (url) => { - mecredApi - .get(url) - .then(({ data }) => { - setLearningObjects(data); - handleResize(data.length); - }); - }; - + }, [cardsPerRow, url]); const toggleContent = () => { setExpanded(!expanded); diff --git a/src/app/components/Header.js b/src/app/components/Header.js index bf31bafb8063795826469591e398f40d584414a4..68a6e993774e9fe1417bb92ffd0a2381341595aa 100644 --- a/src/app/components/Header.js +++ b/src/app/components/Header.js @@ -16,6 +16,7 @@ import SearchIcon from '@mui/icons-material/Search'; import MoreIcon from '@mui/icons-material/MoreVert'; import NotificationsActiveSharpIcon from '@mui/icons-material/NotificationsActiveSharp'; import Link from 'next/link'; +import Image from 'next/image'; import HighlightOffIcon from '@mui/icons-material/HighlightOff'; import AccountMenu from './MenuProfile'; import FilterAltIcon from '@mui/icons-material/FilterAlt'; @@ -131,7 +132,7 @@ export default function Header({ handleOpenMenu }) { > <MenuIcon className='text-4xl text-secondary' /> </IconButton> - <Link href="/"> <img src="/mecred.svg" alt="logo" className="h-10 mt-2 max-xs:max-w-md inline" /> </Link> + <Link href="/"> <Image src="/mecred.svg" alt="logo" width="45" height="40" className="h-10 mt-2 max-xs:max-w-md inline" /> </Link> </div> <div className="flex w-[60%] justify-center max-lg:hidden "> diff --git a/src/app/components/InfiniteScroll.js b/src/app/components/InfiniteScroll.js index 052b5fbf7ab8d8d98ea2056bd5a1ca15f3dd63a3..f8ce56839c39f48ecbe49e7e1ddf883b98411679 100644 --- a/src/app/components/InfiniteScroll.js +++ b/src/app/components/InfiniteScroll.js @@ -7,24 +7,38 @@ export default function InfiniteScroll({ type, filter, setNewSize, newSize }) { const [isLoading, setIsLoading] = useState(false); const [page, setPage] = useState(0); - const fetchData = async (page) => { + const fetchData = useCallback(async (page) => { setIsLoading(true); const url = `/search?page=${page}&results_per_page=20&order=${filter}&query=*&search_class=${type}`; try { - const { data } = await mecredApi - .get(url); - setItems((prevItems) => [...page === 0 ? [] : prevItems, ...data]); + const { data } = await mecredApi.get(url); + setItems((prevItems) => { + // TODO: O backend retorna itens repetidos porque ordena as páginas por + // um atributo que pode repetir. A solução adequada é que o backend ordene + // por mais de um campo, por exemplo pelo filtro passado e depois pelo id, + // assim as ordenações serão estáveis. + // Solução hack atual: remover os itens duplicados. + const idsSeen = new Set(); + const withoutDupes = []; + [...page === 0 ? [] : prevItems, ...data].forEach((item) => { + if (idsSeen.has(item.id)) { return; } + idsSeen.add(item.id); + withoutDupes.push(item); + }); + return withoutDupes; + }); + setPage(() => page + 1); } catch (error) { console.log(error); } finally { setIsLoading(false); } - }; + }, [setItems, setPage, setIsLoading, filter, type]); - const handleScroll = () => { + const handleScroll = useCallback(() => { if ( window.innerHeight + document.documentElement.scrollTop !== document.documentElement.offsetHeight || @@ -33,16 +47,16 @@ export default function InfiniteScroll({ type, filter, setNewSize, newSize }) { return; } fetchData(page); - }; + }, [fetchData, isLoading, page]); useEffect(() => { fetchData(0).then(() => { window.scrollTo(0, 0) }); - }, [type, filter]); + }, [type, filter, fetchData]); useEffect(() => { window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); - }, [isLoading]); + }, [isLoading, handleScroll]); return <InfiniteScrollCards setNewSize={setNewSize} newSize={newSize} data={items} type={type} />; } diff --git a/src/app/components/InfiniteScrollCards.js b/src/app/components/InfiniteScrollCards.js index ee0b19099e800d5b88b2ac9c7ce93a6acaf96395..75fd933aeb2e92ffce490c90df28b5cec66a1247 100644 --- a/src/app/components/InfiniteScrollCards.js +++ b/src/app/components/InfiniteScrollCards.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, use } from "react"; +import React, { useState, useEffect, Fragment } from "react"; import Cards from "./Cards"; import GroupCardsCollections from "./GroupCardsCollections"; import Link from "next/link"; @@ -84,29 +84,28 @@ export default function InfiniteScrollCards({ data, type = "LearningObject", set <Link href="/perfil"> <p className=" ml-0 max-sm:text-center text-gray-500 mb-4" > por Ministério da Educação</p> </Link> - {mecCollection.map((item, index) => { return ( - <> + <Fragment key={item['id']}> <p className="text-xl max-sm:text-center font-bold mb-1 text-gray-600 ml-6 inline-block">{item["name"]}</p> - <GroupCardsCollections boxShadow={"1px 1px 10px 1px #ffbf00"} cardsPerRow={cardsPerRow} data={item['collection_items']} key={item['id']} /> - </> + <GroupCardsCollections boxShadow={"1px 1px 10px 1px #ffbf00"} cardsPerRow={cardsPerRow} data={item['collection_items']}/> + </Fragment> ) })} <p className=" mb-4 text-xl max-sm:text-center font-bold text-gray-600 ml-0 inline-block">Coleções dos Usuários</p> {data.map((item, index) => ( - <> + <Fragment key={item['id']}> <p className="text-xl max-sm:text-center font-bold mb-0 text-gray-600 ml-6 inline-block">{item['name']}</p> - <p className=" ml-6 max-sm:text-center text-gray-500 mb-0" > + <p className=" ml-6 max-sm:text-center text-gray-500 mb-0"> por <Link href="/perfil"> {item["owner"]["name"]}</Link>{timeFunction(item["updated_at"])} </p> - <GroupCardsCollections cardsPerRow={cardsPerRow} data={item['collection_items']} key={item['id']}/> - </> + <GroupCardsCollections cardsPerRow={cardsPerRow} data={item['collection_items']}/> + </Fragment> ))} </div> - } + } </div> ) }; \ No newline at end of file diff --git a/src/app/login/components/LoginForm.js b/src/app/login/components/LoginForm.js index 963d7038ccdf4865ee51c2d4267739f17a56d1d8..10a14815676ddcd6064e6ceac1fddc3e22f469dc 100644 --- a/src/app/login/components/LoginForm.js +++ b/src/app/login/components/LoginForm.js @@ -74,7 +74,7 @@ export default function LoginForm({ variant="outlined" className="mt-2 border-main text-gray-500 normal-case flex gap-2" > - <img className="w-6 h-6" src="/google.svg" alt="google logo" /> + <Image className="w-6 h-6" src="/google.svg" alt="google logo" /> <span>Entrar com o Google</span> </Button> <p className="mt-5 text-xs text-gray-500"> diff --git a/src/app/login/components/SignupModal.js b/src/app/login/components/SignupModal.js index af3409e20c9eb3cc25118ae2f515de8960d38d4e..46edeaaf5b545d7cfb22c4f26715268dea567409 100644 --- a/src/app/login/components/SignupModal.js +++ b/src/app/login/components/SignupModal.js @@ -2,6 +2,7 @@ import * as React from "react"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Modal from "@mui/material/Modal"; +import Image from 'next/image'; import { TextField, Divider, Alert } from "@mui/material"; import { useState } from "react"; import mecredApi from "@/axiosConfig"; @@ -126,7 +127,7 @@ export default function SignupModal({ open, handleClose }) { variant="outlined" className="mt-2 border-main text-gray-500 normal-case flex gap-2" > - <img className="w-6 h-6" src="/google.svg" alt="google logo" /> + <Image className="w-6 h-6" src="/google.svg" alt="google logo" /> <span>Cadastrar-se com o Google</span> </Button> </form> diff --git a/src/app/login/page.js b/src/app/login/page.js index cd2fbd4ecf46349c0f90da3fdda039ab820a562f..bcb3914e3a2637ec7e8c969de4dd0653f5f4a12d 100644 --- a/src/app/login/page.js +++ b/src/app/login/page.js @@ -9,8 +9,9 @@ import { saveToLocalStorage, } from "../handlers/localStorageHandler"; import { redirect, useRouter, useSearchParams } from "next/navigation"; +import { Suspense } from 'react'; -export default function Login() { +function Login() { const [userEmail, setUserEmail] = useState(""); const [userPassword, setUserPassword] = useState(""); const [errorMessage, setErrorMessage] = useState(""); @@ -76,3 +77,11 @@ export default function Login() { </Grid> ); } + +export default function LoginWrapper() { + return ( + <Suspense> + <Login/> + </Suspense> + ); +}; \ No newline at end of file diff --git a/src/app/perfil/components/Favorites.js b/src/app/perfil/components/Favorites.js index 6867637d373c606e6002a5038b18846b6a7e9916..fddbebce4fa6a359993716ff835176cb43116634 100644 --- a/src/app/perfil/components/Favorites.js +++ b/src/app/perfil/components/Favorites.js @@ -12,41 +12,41 @@ export default function Favorites({ id }) { const uid = getStoredValue("uid") useEffect(() => { + const fetchResources = async (id) => { + mecredApi + .get(`/users/${id}/learning_objects/liked`, { + headers: { + 'access-token': token, + 'token-type': 'Bearer', + 'client': client, + 'uid': uid, + 'Expires': 0 + } + }) + .then(({ data }) => { + setResources(data); + }); + } + + const fetchCollections = async (id) => { + mecredApi + .get(`/users/${id}/collections/liked`, { + headers: { + 'access-token': token, + 'token-type': 'Bearer', + 'client': client, + 'uid': uid, + 'Expires': 0 + } + }) + .then(({ data }) => { + setCollections(data); + }); + }; fetchCollections(id); fetchResources(id); - }, []); + }, [client, id, token, uid]); - const fetchResources = async (id) => { - mecredApi - .get(`/users/${id}/learning_objects/liked`, { - headers: { - 'access-token': token, - 'token-type': 'Bearer', - 'client': client, - 'uid': uid, - 'Expires': 0 - } - }) - .then(({ data }) => { - setResources(data); - }); - } - - const fetchCollections = async (id) => { - mecredApi - .get(`/users/${id}/collections/liked`, { - headers: { - 'access-token': token, - 'token-type': 'Bearer', - 'client': client, - 'uid': uid, - 'Expires': 0 - } - }) - .then(({ data }) => { - setCollections(data); - }); - }; return ( <> <Card className='p-3 my-10 rounded-md min-w-[200px] min-h-[180px] ' > diff --git a/src/app/perfil/components/ProfileHomologation.js b/src/app/perfil/components/ProfileHomologation.js index 57b405a5266edce1f371bb1943bc0f5a083b6f8d..17e8c3fc7a90ef2ef42396db5eecb4afb99f1e83 100644 --- a/src/app/perfil/components/ProfileHomologation.js +++ b/src/app/perfil/components/ProfileHomologation.js @@ -16,9 +16,26 @@ export default function Favorites({ id }) { const uid = getStoredValue("uid") useEffect(() => { - fetchHomologation(id); + const fetchHomologation = async (id, offset) => { + mecredApi + .get(`/submissions/all_users_submissions/${id}?offset=${numberCards}`, { + headers: { + 'access-token': token, + 'token-type': 'Bearer', + 'client': client, + 'uid': uid, + 'Expires': 0 + } + }) + .then(({ data }) => { + setHomologated(prevData => [...prevData, ...data]); + handleResize(data.length) + // setNumberCards(data.length) + }); + } - }, [numberCards]); + fetchHomologation(id); + }, [client, id, token, numberCards, uid]); const toggleContent = () => { setNumberCards(numberCards + 12) @@ -31,25 +48,6 @@ export default function Favorites({ id }) { } }; - const fetchHomologation = async (id, offset) => { - mecredApi - .get(`/submissions/all_users_submissions/${id}?offset=${numberCards}`, { - headers: { - 'access-token': token, - 'token-type': 'Bearer', - 'client': client, - 'uid': uid, - 'Expires': 0 - } - }) - .then(({ data }) => { - setHomologated(prevData => [...prevData, ...data]); - handleResize(data.length) - // setNumberCards(data.length) - }); - } - - return ( <Card className='p-3 my-10 rounded-md min-w-[200px] min-h-[180px] ' > <div className="text-gray-500 text-2xl font-semibold ">Homologação</div> diff --git a/src/app/perfil/components/ProfileOptions.js b/src/app/perfil/components/ProfileOptions.js index 0f9979d1cf801e60e630c3b02d818c59b0785fab..b015a68dc44acaaf3fce098f3e672bfd1540f72c 100644 --- a/src/app/perfil/components/ProfileOptions.js +++ b/src/app/perfil/components/ProfileOptions.js @@ -12,25 +12,24 @@ export default function ProfileOptions ({ title, content, optButton, id }){ const [resources, setResources] = useState([]) useEffect(() => { + const fetchResources = async (id) => { + mecredApi + .get(`/users/${id}/learning_objects`) + .then(({ data }) => { + setResources(data); + }); + } + + const fetchCollections = async (id) => { + mecredApi + .get(`/users/${id}/collections`) + .then(({ data }) => { + setCollections(data); + }); + }; fetchCollections(id); fetchResources(id); - }, []); - - const fetchResources = async (id) => { - mecredApi - .get(`/users/${id}/learning_objects`) - .then(({ data }) => { - setResources(data); - }); - } - - const fetchCollections = async (id) => { - mecredApi - .get(`/users/${id}/collections`) - .then(({ data }) => { - setCollections(data); - }); - }; + }, [id]); const opts = (op) => { switch(op){ diff --git a/src/app/perfil/components/UserCard.js b/src/app/perfil/components/UserCard.js index e4821f09e85f819e0a7ac1c91b4977e7f874c954..20c69932bbebc37d3afb218d9ed6ac54c963e3fd 100644 --- a/src/app/perfil/components/UserCard.js +++ b/src/app/perfil/components/UserCard.js @@ -73,71 +73,62 @@ export default function UserCard({ profileData }) { const [translateItems, setTranslateItems] = useState([]) const [optButton, setOptButton] = useState(0) - const fetchFollowers = () => { - const token = getStoredValue("access_token") - const client = getStoredValue("client") - const uid = getStoredValue("uid") - const expiry = getStoredValue("expiry") - - - mecredApi.get('/users/' + profileData["id"] + "/followers", { - headers: { - 'access-token': token, - 'token-type': 'Bearer', - 'client': client, - 'uid': uid, - 'Expires': 0 - } - - }) - .then(({ data }) => setFollowers(data)) - .catch((error) => { - console.error(error) - }) - - mecredApi.get('/users/' + profileData["id"] + "/following/User?offset=none", { - headers: { - 'access-token': token, - 'token-type': 'Bearer', - 'client': client, - 'uid': uid, - 'Expires': 0 - } - - }) - .then(({ headers, data }) => { - // setFollowing(data); - setFollowing(Number(headers["x-total-count"])) + useEffect(() => { + const fetchFollowers = () => { + const token = getStoredValue("access_token") + const client = getStoredValue("client") + const uid = getStoredValue("uid") + const expiry = getStoredValue("expiry") + + mecredApi.get('/users/' + profileData["id"] + "/followers", { + headers: { + 'access-token': token, + 'token-type': 'Bearer', + 'client': client, + 'uid': uid, + 'Expires': 0 + } + }) - .catch((error) => { - console.error(error) + .then(({ data }) => setFollowers(data)) + .catch((error) => { + console.error(error) + }) + + mecredApi.get('/users/' + profileData["id"] + "/following/User?offset=none", { + headers: { + 'access-token': token, + 'token-type': 'Bearer', + 'client': client, + 'uid': uid, + 'Expires': 0 + } + }) - } - - useEffect(() => { + .then(({ headers, data }) => { + // setFollowing(data); + setFollowing(Number(headers["x-total-count"])) + }) + .catch((error) => { + console.error(error) + }) + } fetchFollowers() - - }, []) - - - const itemsRoles = async () => { - - - let items = [] - for (let i = 0; i < profileData["roles"].length; i++) { - let found = roles.find((element) => profileData["roles"][i]["name"] === element.role) - - if (!items.includes(found.translate) && found.role !== "submitter") items.push(found.translate) + + const itemsRoles = async () => { + let items = [] + for (let i = 0; i < profileData["roles"].length; i++) { + let found = roles.find((element) => profileData["roles"][i]["name"] === element.role) + + if (!items.includes(found.translate) && found.role !== "submitter") items.push(found.translate) + } + return items; } - return items; - - } - - useEffect(() => { + itemsRoles() .then(roles => setTranslateItems(roles)) - }, []) + }, [profileData]) return ( <>