Commit 34385e4e authored by Lucas  Ferreira Rodrigues's avatar Lucas Ferreira Rodrigues

Merge branch '4-estrutura-inicial-tela-conteudo' into 'develop'

Resolve "Estrutura inicial tela Conteúdo"

See merge request !4
parents 3e24d174 2dae3f56
......@@ -15,7 +15,8 @@
"next": "15.0.3",
"prop-types": "^15.8.1",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106"
"react-dom": "19.0.0-rc-66855b96-20241106",
"react-player": "^2.16.0"
},
"devDependencies": {
"autoprefixer": "^10.4.20",
......@@ -1306,6 +1307,15 @@
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
......@@ -1803,6 +1813,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/load-script": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz",
"integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==",
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
......@@ -1837,6 +1853,12 @@
"dev": true,
"license": "ISC"
},
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"license": "MIT"
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
......@@ -2370,12 +2392,34 @@
"react": "19.0.0-rc-66855b96-20241106"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
"license": "MIT"
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/react-player": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz",
"integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==",
"license": "MIT",
"dependencies": {
"deepmerge": "^4.0.0",
"load-script": "^1.0.0",
"memoize-one": "^5.1.1",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.0.1"
},
"peerDependencies": {
"react": ">=16.6.0"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
......
public/check.png

10.2 KB

public/conteudo.png

2.26 KB

public/pdf-image.png

81.3 KB

public/x.png

31 KB

......@@ -2,35 +2,215 @@
import { useQuery } from "@tanstack/react-query";
import { fetchApi } from "@/utils/fetchApi";
import { useSearchParams } from 'next/navigation'
import { useState } from "react";
import env from "dotenv"
import React from "react";
import ReactPlayer from "react-player/youtube";
import Button from "@/components/button";
import Submit from "@/components/Submit";
import Image from "next/image";
import ErrorModal from "@/components/ErrorModal";
env.config();
function baixarArquivo(url) {
window.open(`/api/arquivo?path=${encodeURIComponent(url)}`, '_blank');
};
export default function Aula() {
const { isLoading, error, data } = useQuery({
queryKey: ['repoData'],
queryFn: async () => {
const response = await fetchApi(process.env.NEXT_PUBLIC_API_URL + 'aula')
const [modal, setModal] = useState(true)
const [index, setIndex] = useState(0)
if (response.isError) {
throw response;
} else {
return response;
}
},
})
const searchParams = useSearchParams()
const modulo_id = searchParams.get('modulo_id')
if (isLoading) return <span>Carregando...</span>
const { isLoading, error, data } = useQuery({
queryKey: ['repoData'],
queryFn: async () => {
const response = await fetchApi(process.env.NEXT_PUBLIC_API_URL + 'aula?perPage=1000&modulo_id=' + modulo_id)
if (error) return <span>Ocorreu um erro: {error.message}</span>
if (response.isError) {
throw response;
} else {
return response;
}
},
})
if (data && data.data.error) {
return (
<div className="flex flex-col gap-4">
{console.log(data && data)}
<Button texto="Enviar" largura="10rem"> </Button>
<Submit texto="Enviar" largura="10rem"> </Submit>
<ErrorModal
errorCode={data.data.code}
errorMessage={data.data.errors[0].message}
/>
)
}
const { isLoading: isLoadingArquivos, error: errorArquivos, data: dataArquivos } = useQuery({
queryKey: ['arquivosData', index],
queryFn: async () => {
const response = await fetchApi(process.env.NEXT_PUBLIC_API_URL + 'arquivos?aula_id=' + data.data.data[index].id);
if (response.isError) {
throw response;
} else {
return response;
}
},
enabled: !isLoading,
});
if (dataArquivos && dataArquivos.data.error) {
return (
<ErrorModal
errorCode={dataArquivos.data.code}
errorMessage={dataArquivos.data.errors[0].message}
/>
)
}
if (dataArquivos && dataArquivos.isError || data && data.isError) {
return (
<ErrorModal
errorCode="500"
errorMessage="Ocorreu um erro interno."
/>
)
}
if (isLoading || isLoadingArquivos) {
return (
<main>
<div className={modal ? "w-3/4 overflow-hidden" : "w-full"}>
<div className="flex items-center justify-center h-screen">
<div className="border-t-4 border-verde border-solid w-16 h-16 rounded-full animate-spin"></div>
</div>
</div>
<div className={`fixed top-0 right-0 h-screen w-1/4 bg-white border-l-2 border-gray-200 shadow-lg z-50 ${modal ? "block" : "hidden"}`}>
<div className="flex items-center justify-between p-9 border-cinza_c2 border-b-2">
</div>
<div className="flex items-center justify-center h-screen">
<div className="border-t-4 border-verde border-solid w-16 h-16 rounded-full animate-spin"></div>
</div>
</div>
</main>
)
}
const nextAula = () => {
if (data && index < data.data.data.length - 1) {
setIndex(index + 1);
}
};
return (
<main className="flex w-full">
<div className={modal ? "w-3/4 overflow-hidden" : "w-full"}>
<div className="flex flex-col gap-4">
<ReactPlayer
url={(data.data.data[index].video).split('&list=')[0]}
controls
width="100%"
height="700px"
/>
<button
type="button"
className={modal ? "hidden" : "block"}
onClick={() => { setModal(true) }}
>
<Image
className="absolute top-20 -right-24 z-50 opacity-50 hover:opacity-90 hover:right-0 duration-500"
src="/conteudo.png"
alt="botão de abrir o modal."
width={130}
height={40}
/>
</button>
</div>
<div className={modal ? "" : "w-full flex items-center justify-center"}>
<div className={modal ? "" : "w-9/12"}>
<div className="flex items-center justify-between p-3">
<h1 className="w-full text-center text-2xl">{data && data.data.data[index].titulo}</h1>
<Button className="w-1/12" texto="Finalizar" largura="10rem" onClick={nextAula}></Button>
</div>
<p className="text-justify p-3">{data && data.data.data[index].descricao}</p>
<div>
<h2 className="pl-3 text-xl">Conteúdo em arquivo</h2>
{dataArquivos && dataArquivos.data.data?.map((arquivo) => {
if (arquivo.tipo == "conteúdo") {
return (
<div key={arquivo.url} className="flex items-center p-3">
<Image
src="/pdf-image.png"
alt="imagem se um pdf."
width={24}
height={24}
/>
<button type="button" onClick={() => baixarArquivo(arquivo.url)} className="pl-2 text-sm">
{arquivo.url.split('/').pop()}
</button>
</div>
);
}
return null
})}
<h3 className="pt-3 pl-3">Gabarito</h3>
{dataArquivos && dataArquivos.data.data?.map((arquivo) => {
if (arquivo.tipo == "gabarito") {
return (
<div key={arquivo.url} className="flex items-center p-3">
<Image
src="/pdf-image.png"
alt="imagem se um pdf."
width={24}
height={24}
/>
<button type="button" onClick={() => baixarArquivo(arquivo.url)} className="pl-2 text-sm">
{arquivo.url.split('/').pop()}
</button>
</div>
);
}
return null
})}
</div>
</div>
</div>
</div>
{/* Modal */}
<div className={`fixed top-0 right-0 h-screen w-1/4 bg-white border-l-2 border-gray-200 shadow-lg z-50 ${modal ? "block" : "hidden"}`}>
<div className="flex items-center justify-between p-5 border-cinza_c2 border-b-2">
<p className="text-xl">Conteúdo do Modulo</p>
<button type="button" onClick={() => { setModal(false) }}>
<Image
className="mr-5"
src="/x.png"
alt="Fechar Modal"
width={18}
height={18}
/>
</button>
</div>
{/* Adicionando rolagem no conteúdo do modal */}
<div className="overflow-y-auto h-full">
{data && data.data.data?.map((aula, index) => (
<div className="flex items-center justify-start p-3" key={index}>
<Image
src="/check.png"
alt="imagem se um pdf."
width={18}
height={18}
/>
<button className="ml-3 text-left" onClick={() => { setIndex(index) }}>
{aula.titulo}
</button>
</div>
))}
</div>
</div>
</main>
)
}
\ No newline at end of file
import env from "dotenv";
env.config();
export async function GET(req) {
const { searchParams } = new URL(req.url);
// URL da API com o caminho fornecido como parâmetro
const path = searchParams.get('path');
const fileUrl = `${process.env.NEXT_PUBLIC_API_URL}arquivos/${path}`;
try {
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error('Erro ao obter o arquivo');
}
const contentType = response.headers.get('Content-Type');
const contentDisposition = response.headers.get('Content-Disposition');
const buffer = await response.arrayBuffer();
return new Response(Buffer.from(buffer), {
headers: {
'Content-Type': contentType || 'application/octet-stream',
'Content-Disposition': contentDisposition || 'inline',
},
});
} catch (error) {
console.error('Erro ao obter o arquivo:', error);
return new Response(JSON.stringify({ message: 'Erro ao obter o arquivo' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
import React from 'react';
import { useRouter } from 'next/navigation';
const ErrorModal = ({ errorCode, errorMessage, onClose }) => {
const router = useRouter();
const handleClose = () => {
// Redireciona para a página de "modulos" quando o usuário clicar em "Fechar"
router.push('/modulos');
// Chama a função onClose (caso seja necessário)
if (onClose) {
onClose();
}
};
return (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
<div className="bg-white p-6 rounded-lg shadow-lg w-80">
<h2 className="text-xl font-bold text-red-600">Erro</h2>
<p className="mt-4 text-sm">Código do erro: <strong>{errorCode}</strong></p>
<p className="mt-2 text-sm">Mensagem: {errorMessage}</p>
<div className="mt-4 flex justify-end">
<button
className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
onClick={handleClose}
>
Fechar
</button>
</div>
</div>
</div>
);
};
export default ErrorModal;
......@@ -2,9 +2,10 @@ import PropTypes from 'prop-types';
import '../index.css';
const Submit = ({ texto, largura }) => {
const Submit = ({ texto, largura, onClick }) => {
return (
<button type="submit"
onClick={onClick}
style={{ width: largura }}
className="flex
items-center
......
......@@ -2,10 +2,11 @@ import PropTypes from 'prop-types';
import '../index.css';
const Button = ({ texto, largura }) => {
const Button = ({ texto, largura, onClick }) => {
return (
<button
type='button'
onClick={onClick}
style={{ width: largura }}
className="btn
h-10 border
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment