From 354e05c59bde6ef890761d5083cd3923df6a9f80 Mon Sep 17 00:00:00 2001 From: Joa0V Date: Fri, 23 Aug 2024 21:52:37 -0300 Subject: [PATCH 01/24] feat(#78):criando componente JourneyInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Criando componente de informação da jornada Co-authored-by: Nanashii76 Co-authored-by: joaobisi --- src/components/journey/journeyInfo.tsx | 88 ++++++++++++++++++++++++++ src/styles/globals.css | 1 + 2 files changed, 89 insertions(+) create mode 100644 src/components/journey/journeyInfo.tsx diff --git a/src/components/journey/journeyInfo.tsx b/src/components/journey/journeyInfo.tsx new file mode 100644 index 0000000..f6e4ef0 --- /dev/null +++ b/src/components/journey/journeyInfo.tsx @@ -0,0 +1,88 @@ +'use client' + +import React from 'react'; +import { Box, Button, Typography } from '@mui/material'; +import CollectionsBookmarkIcon from '@mui/icons-material/CollectionsBookmark'; +import PercentIcon from '@mui/icons-material/Percent'; +import { PeopleAlt } from '@mui/icons-material'; + +interface JourneyInfoProps { + title: string; + description: string; + trailCount: number; + completionPercentage: number; + completedStudents: number; +} +const JourneyInfo: React.FC = ({ + title, + description, + trailCount, + completionPercentage, + completedStudents +}) => { + + return( + + + {title} + + +

{description}

+
+ + + + + Número de trilhas: {trailCount} + + + + + Porcentagem de conteúdo completada: {completionPercentage} + + + + + Alunos que completaram a jornada: {completedStudents} + + +
+ ); +} + +export default JourneyInfo; \ No newline at end of file diff --git a/src/styles/globals.css b/src/styles/globals.css index 19fe880..2119448 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,6 +1,7 @@ /* Estilos gerais */ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;200;300;400;500;600;700;800;900&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Old+Standard+TT:wght@400;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); @import 'tailwindcss/base'; @import 'tailwindcss/components'; From 0588f110e638112916863b15658ec012cd647934 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 23 Aug 2024 23:26:49 -0300 Subject: [PATCH 02/24] =?UTF-8?q?feat(#78)/progresso-da-trilha:=20Criando?= =?UTF-8?q?=20p=C3=A1gina=20da=20Jornada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Criando página de ingressão e visualização de progresso Co-authored-by: Joa0V Co-authored-by: joaobisi --- src/app/components/sidebar.component.tsx | 61 ------- src/app/journey-page/page.tsx | 31 ++++ src/components/journey/journeyInfo.tsx | 169 +++++++++++------- .../components/studio/MarkdownEditor.tsx | 0 .../components/studio/MarkdownPage.tsx | 0 .../components/studio/MarkdownPreview.tsx | 0 .../components/studio/MarkdownSidebar.tsx | 0 .../components/studio/MarkdownToolbar.tsx | 0 .../studio/hooks/useMarkdownEditor.tsx | 0 .../studio/utils/insertTextAtSelection.tsx | 0 10 files changed, 135 insertions(+), 126 deletions(-) delete mode 100644 src/app/components/sidebar.component.tsx create mode 100644 src/app/journey-page/page.tsx rename src/{app => }/components/studio/MarkdownEditor.tsx (100%) rename src/{app => }/components/studio/MarkdownPage.tsx (100%) rename src/{app => }/components/studio/MarkdownPreview.tsx (100%) rename src/{app => }/components/studio/MarkdownSidebar.tsx (100%) rename src/{app => }/components/studio/MarkdownToolbar.tsx (100%) rename src/{app => }/components/studio/hooks/useMarkdownEditor.tsx (100%) rename src/{app => }/components/studio/utils/insertTextAtSelection.tsx (100%) diff --git a/src/app/components/sidebar.component.tsx b/src/app/components/sidebar.component.tsx deleted file mode 100644 index 3adf535..0000000 --- a/src/app/components/sidebar.component.tsx +++ /dev/null @@ -1,61 +0,0 @@ -'use client'; - -import Link from 'next/link'; -import HomeIcon from '@mui/icons-material/Home'; -import DashboardIcon from '@mui/icons-material/Dashboard'; -import EditNoteIcon from '@mui/icons-material/EditNote'; -import { Drawer, IconButton, Box } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; -import { useSession } from 'next-auth/react'; - -interface SideBarProps { - handleDrawerOpen: () => void; - open: boolean; -} - -const Sidebar: React.FC = ({ handleDrawerOpen, open }) => { - const session = useSession(); - - return ( - - - - - -
    -
  • - - - Home - -
  • - {session.data?.user.role === 'admin' && ( -
  • - - - Painel de Administrador - -
  • - )} -
  • - - - Estúdio de Criação - -
  • -
-
-
- ); -}; - -export default Sidebar; diff --git a/src/app/journey-page/page.tsx b/src/app/journey-page/page.tsx new file mode 100644 index 0000000..979e508 --- /dev/null +++ b/src/app/journey-page/page.tsx @@ -0,0 +1,31 @@ +'use client'; +import JourneyInfo from '../../components/journey/journeyInfo'; +import { Box, Divider } from '@mui/material'; + +export default function JourneyPage() { + return ( + + + + + + + + + + + + ); +} diff --git a/src/components/journey/journeyInfo.tsx b/src/components/journey/journeyInfo.tsx index f6e4ef0..2b9f72e 100644 --- a/src/components/journey/journeyInfo.tsx +++ b/src/components/journey/journeyInfo.tsx @@ -1,88 +1,127 @@ -'use client' +'use client'; import React from 'react'; import { Box, Button, Typography } from '@mui/material'; import CollectionsBookmarkIcon from '@mui/icons-material/CollectionsBookmark'; import PercentIcon from '@mui/icons-material/Percent'; import { PeopleAlt } from '@mui/icons-material'; +import MyButton from '@/components/ui/buttons/myButton.component'; interface JourneyInfoProps { - title: string; - description: string; - trailCount: number; - completionPercentage: number; - completedStudents: number; + title: string; + description: string; + trailCount: number; + completionPercentage: number; + completedStudents: number; } const JourneyInfo: React.FC = ({ - title, - description, - trailCount, - completionPercentage, - completedStudents + title, + description, + trailCount, + completionPercentage, + completedStudents, }) => { + return ( + + + {title} + - return( + +

{description}

+
- + - {title} - - -

{description}

+
- - Número de trilhas: {trailCount} + + - - - Número de trilhas: {trailCount} - - + - - - Porcentagem de conteúdo completada: {completionPercentage} + alignItems: 'center', + }} + > + - + Porcentagem de conteúdo completada: {completionPercentage} + + + - - - Alunos que completaram a jornada: {completedStudents} + mb={1} + > + + - - ); -} + + Alunos que completaram a jornada: {completedStudents} + + + + + Ingressar + + +
+ ); +}; -export default JourneyInfo; \ No newline at end of file +export default JourneyInfo; diff --git a/src/app/components/studio/MarkdownEditor.tsx b/src/components/studio/MarkdownEditor.tsx similarity index 100% rename from src/app/components/studio/MarkdownEditor.tsx rename to src/components/studio/MarkdownEditor.tsx diff --git a/src/app/components/studio/MarkdownPage.tsx b/src/components/studio/MarkdownPage.tsx similarity index 100% rename from src/app/components/studio/MarkdownPage.tsx rename to src/components/studio/MarkdownPage.tsx diff --git a/src/app/components/studio/MarkdownPreview.tsx b/src/components/studio/MarkdownPreview.tsx similarity index 100% rename from src/app/components/studio/MarkdownPreview.tsx rename to src/components/studio/MarkdownPreview.tsx diff --git a/src/app/components/studio/MarkdownSidebar.tsx b/src/components/studio/MarkdownSidebar.tsx similarity index 100% rename from src/app/components/studio/MarkdownSidebar.tsx rename to src/components/studio/MarkdownSidebar.tsx diff --git a/src/app/components/studio/MarkdownToolbar.tsx b/src/components/studio/MarkdownToolbar.tsx similarity index 100% rename from src/app/components/studio/MarkdownToolbar.tsx rename to src/components/studio/MarkdownToolbar.tsx diff --git a/src/app/components/studio/hooks/useMarkdownEditor.tsx b/src/components/studio/hooks/useMarkdownEditor.tsx similarity index 100% rename from src/app/components/studio/hooks/useMarkdownEditor.tsx rename to src/components/studio/hooks/useMarkdownEditor.tsx diff --git a/src/app/components/studio/utils/insertTextAtSelection.tsx b/src/components/studio/utils/insertTextAtSelection.tsx similarity index 100% rename from src/app/components/studio/utils/insertTextAtSelection.tsx rename to src/components/studio/utils/insertTextAtSelection.tsx From 6da22c85ed55849247e8eee10a8dde2373203177 Mon Sep 17 00:00:00 2001 From: Joa0V Date: Sat, 24 Aug 2024 17:29:48 -0300 Subject: [PATCH 03/24] feat(#78):Adicionando trails a interface de Journey e contagem de trilhas na journey-page Co-authored-by: Nanashii76 Co-authored-by: joaobisi --- src/app/journey-page/[journeyId]/page.tsx | 56 +++++++++++++++++++++++ src/app/journey-page/page.tsx | 31 ------------- src/components/journey/journeyInfo.tsx | 2 +- src/lib/interfaces/journey.interface.ts | 1 + 4 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 src/app/journey-page/[journeyId]/page.tsx delete mode 100644 src/app/journey-page/page.tsx diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx new file mode 100644 index 0000000..b6df3e7 --- /dev/null +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -0,0 +1,56 @@ +'use client'; +import JourneyInfo from '../../../components/journey/journeyInfo'; +import { Box, Divider } from '@mui/material'; +import { getJourney } from '../../../services/studioMaker.service' +import { Journey } from '@/lib/interfaces/journey.interface'; +import { useEffect, useState } from 'react'; +import { useParams } from 'next/navigation'; + +export default function JourneyPage() { + + const { journeyId } = useParams(); + const [journey, setJourney] = useState({} as Journey); + const [error, setError] = useState(null); + + useEffect(() => { + + const id = Array.isArray(journeyId) ? journeyId[0] : journeyId; + const fetchJourney = async () => { + try{ + const response = await getJourney(id); + setJourney(response); + } + catch(err){ + setError('Failed to fetch journey'); + } + } + + fetchJourney(); + }, [journeyId]); + + return ( + + + + + + + + + + + + ); +} diff --git a/src/app/journey-page/page.tsx b/src/app/journey-page/page.tsx deleted file mode 100644 index 979e508..0000000 --- a/src/app/journey-page/page.tsx +++ /dev/null @@ -1,31 +0,0 @@ -'use client'; -import JourneyInfo from '../../components/journey/journeyInfo'; -import { Box, Divider } from '@mui/material'; - -export default function JourneyPage() { - return ( - - - - - - - - - - - - ); -} diff --git a/src/components/journey/journeyInfo.tsx b/src/components/journey/journeyInfo.tsx index 2b9f72e..24a92a4 100644 --- a/src/components/journey/journeyInfo.tsx +++ b/src/components/journey/journeyInfo.tsx @@ -106,7 +106,7 @@ const JourneyInfo: React.FC = ({
- Alunos que completaram a jornada: {completedStudents} + Alunos que ingressaram a jornada: {completedStudents}
Date: Sat, 24 Aug 2024 20:41:51 -0300 Subject: [PATCH 04/24] =?UTF-8?q?feat(#78)/progresso-da-trilha:=20Cria?= =?UTF-8?q?=C3=A7=C3=A3o=20da=20p=C3=A1gina=20de=20ingress=C3=A3o=20de=20t?= =?UTF-8?q?rilha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionando protótipo de animação de progresso de trilha Co-authored-by: joaobisi Co-authored-by: Joa0V --- src/app/journey-page/[journeyId]/page.tsx | 145 ++++++++++++++++++++-- 1 file changed, 135 insertions(+), 10 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index b6df3e7..617a7e0 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -1,33 +1,51 @@ 'use client'; import JourneyInfo from '../../../components/journey/journeyInfo'; import { Box, Divider } from '@mui/material'; -import { getJourney } from '../../../services/studioMaker.service' +import { getJourney } from '../../../services/studioMaker.service'; import { Journey } from '@/lib/interfaces/journey.interface'; import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; -export default function JourneyPage() { +const defaultTrails = [ + { id: '1', isConnected: true }, + { id: '2', isConnected: true }, + { id: '3', isConnected: false }, + { id: '4', isConnected: true }, + { id: '5', isConnected: false }, +]; +export default function JourneyPage() { const { journeyId } = useParams(); const [journey, setJourney] = useState({} as Journey); const [error, setError] = useState(null); useEffect(() => { - + const styleSheet = document.createElement('style'); + styleSheet.type = 'text/css'; + styleSheet.innerText = styles; + document.head.appendChild(styleSheet); + + return () => { + document.head.removeChild(styleSheet); + }; + }, []); + + useEffect(() => { const id = Array.isArray(journeyId) ? journeyId[0] : journeyId; const fetchJourney = async () => { - try{ + try { const response = await getJourney(id); setJourney(response); - } - catch(err){ - setError('Failed to fetch journey'); + } catch (err) { + setError('Failed to fetch journey'); } } fetchJourney(); }, [journeyId]); + const nodeSpacing = 120; // Vertical spacing between nodes + return ( - + - - + + {defaultTrails.map((trail, index) => ( + + ))} + {defaultTrails.map((trail, index) => { + if (index < defaultTrails.length - 1 && defaultTrails[index].isConnected && defaultTrails[index + 1].isConnected) { + const startTop = 50 + index * nodeSpacing; + const endTop = 50 + (index + 1) * nodeSpacing; + const height = Math.abs(endTop - startTop); + return ( + + ); + } + return null; + })} ); } + +const styles = ` + @keyframes bounce { + 0%, 20%, 50%, 80%, 100% { + transform: translateY(0); + } + 40% { + transform: translateY(-30px); + } + 60% { + transform: translateY(-15px); + } + } + + @keyframes shine { + 0% { + box-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + 50% { + box-shadow: 0 0 15px rgba(0, 255, 0, 1); + } + 100% { + box-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + } + + .node { + width: 80px; + height: 80px; + background-color: #4caf50; /* Green color */ + border-radius: 50%; + animation: bounce 2s infinite, shine 2s ease-in-out infinite; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: bold; + text-align: center; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + border: 3px solid #388e3c; /* Darker green border */ + } + + .node::after { + content: attr(data-label); + font-size: 12px; + display: block; + margin-top: 10px; + } + + .edge { + position: absolute; + width: 2px; + background-color: lightgreen; + transform-origin: top; + border-radius: 4px; + animation: growEdge 2s ease-out forwards; + } + + .disconnected { + background-color: #9e9e9e; /* Gray color */ + animation: shine 3s ease-in-out infinite; + } +`; From e290140edf4519ff26a0b07746f828c418b29def Mon Sep 17 00:00:00 2001 From: joaobisi Date: Mon, 26 Aug 2024 15:28:15 -0300 Subject: [PATCH 05/24] Inclusao do caminho das trilhas de uma jornada --- src/app/journey-page/[journeyId]/page.tsx | 235 ++++++---------------- src/components/journey/journeyInfo.tsx | 50 ----- src/components/journey/journeyPath.tsx | 49 +++++ 3 files changed, 110 insertions(+), 224 deletions(-) create mode 100644 src/components/journey/journeyPath.tsx diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index 617a7e0..33f6889 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -1,181 +1,68 @@ 'use client'; -import JourneyInfo from '../../../components/journey/journeyInfo'; + +import React, { useEffect, useState } from 'react'; import { Box, Divider } from '@mui/material'; -import { getJourney } from '../../../services/studioMaker.service'; +import JourneyInfo from '@/components/journey/journeyInfo'; +import JourneyPath from '@/components/journey/journeyPath'; +import { getJourney, getTrails } from '@/services/studioMaker.service'; import { Journey } from '@/lib/interfaces/journey.interface'; -import { useEffect, useState } from 'react'; +import { Trail } from '@/lib/interfaces/trails.interface'; import { useParams } from 'next/navigation'; -const defaultTrails = [ - { id: '1', isConnected: true }, - { id: '2', isConnected: true }, - { id: '3', isConnected: false }, - { id: '4', isConnected: true }, - { id: '5', isConnected: false }, -]; - export default function JourneyPage() { - const { journeyId } = useParams(); - const [journey, setJourney] = useState({} as Journey); - const [error, setError] = useState(null); - - useEffect(() => { - const styleSheet = document.createElement('style'); - styleSheet.type = 'text/css'; - styleSheet.innerText = styles; - document.head.appendChild(styleSheet); - - return () => { - document.head.removeChild(styleSheet); - }; - }, []); - - useEffect(() => { - const id = Array.isArray(journeyId) ? journeyId[0] : journeyId; - const fetchJourney = async () => { - try { - const response = await getJourney(id); - setJourney(response); - } catch (err) { - setError('Failed to fetch journey'); - } - } - - fetchJourney(); - }, [journeyId]); - - const nodeSpacing = 120; // Vertical spacing between nodes - - return ( - - - - - - - - - {defaultTrails.map((trail, index) => ( - - ))} - {defaultTrails.map((trail, index) => { - if (index < defaultTrails.length - 1 && defaultTrails[index].isConnected && defaultTrails[index + 1].isConnected) { - const startTop = 50 + index * nodeSpacing; - const endTop = 50 + (index + 1) * nodeSpacing; - const height = Math.abs(endTop - startTop); - return ( - - ); - } - return null; - })} - - - ); + const { journeyId } = useParams(); + const [journey, setJourney] = useState(null); + const [trails, setTrails] = useState([]); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchJourneyData = async () => { + try { + const id = Array.isArray(journeyId) ? journeyId[0] : journeyId; + const token = JSON.parse(localStorage.getItem('token')!); + + const journeyData = await getJourney(id); + setJourney(journeyData); + + const trailsData = await getTrails({ id, token }); + setTrails(trailsData); + } catch (err) { + setError('Failed to fetch journey data'); + } + }; + + fetchJourneyData(); + }, [journeyId]); + + if (error) { + return
{error}
; + } + + if (!journey || !trails.length) { + return
Loading...
; + } + + return ( + + + + + + + + + + ); } - -const styles = ` - @keyframes bounce { - 0%, 20%, 50%, 80%, 100% { - transform: translateY(0); - } - 40% { - transform: translateY(-30px); - } - 60% { - transform: translateY(-15px); - } - } - - @keyframes shine { - 0% { - box-shadow: 0 0 5px rgba(0, 255, 0, 0.5); - } - 50% { - box-shadow: 0 0 15px rgba(0, 255, 0, 1); - } - 100% { - box-shadow: 0 0 5px rgba(0, 255, 0, 0.5); - } - } - - .node { - width: 80px; - height: 80px; - background-color: #4caf50; /* Green color */ - border-radius: 50%; - animation: bounce 2s infinite, shine 2s ease-in-out infinite; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-weight: bold; - text-align: center; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); - border: 3px solid #388e3c; /* Darker green border */ - } - - .node::after { - content: attr(data-label); - font-size: 12px; - display: block; - margin-top: 10px; - } - - .edge { - position: absolute; - width: 2px; - background-color: lightgreen; - transform-origin: top; - border-radius: 4px; - animation: growEdge 2s ease-out forwards; - } - - .disconnected { - background-color: #9e9e9e; /* Gray color */ - animation: shine 3s ease-in-out infinite; - } -`; diff --git a/src/components/journey/journeyInfo.tsx b/src/components/journey/journeyInfo.tsx index 24a92a4..ac7797b 100644 --- a/src/components/journey/journeyInfo.tsx +++ b/src/components/journey/journeyInfo.tsx @@ -3,23 +3,17 @@ import React from 'react'; import { Box, Button, Typography } from '@mui/material'; import CollectionsBookmarkIcon from '@mui/icons-material/CollectionsBookmark'; -import PercentIcon from '@mui/icons-material/Percent'; -import { PeopleAlt } from '@mui/icons-material'; import MyButton from '@/components/ui/buttons/myButton.component'; interface JourneyInfoProps { title: string; description: string; trailCount: number; - completionPercentage: number; - completedStudents: number; } const JourneyInfo: React.FC = ({ title, description, trailCount, - completionPercentage, - completedStudents, }) => { return ( = ({ Número de trilhas: {trailCount} - - - - - - - Porcentagem de conteúdo completada: {completionPercentage} - - - - - - - - - Alunos que ingressaram a jornada: {completedStudents} - - = ({ trails }) => { + const nodeSpacing = 120; + + return ( + + {trails.map((trail, index) => ( + + ))} + + ); +}; + +export default JourneyPath; From 3691a9e8ab8a9e53cbb77d96907694be68cef3a3 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Mon, 26 Aug 2024 17:25:32 -0300 Subject: [PATCH 06/24] feat(#78): adicionando logica para usuario ingressar na jornada --- src/app/journey-page/[journeyId]/page.tsx | 27 ++++++++++++--- src/components/journey/journeyInfo.tsx | 41 ++++++++++++++++------- src/services/studioMaker.service.ts | 19 +++++++++++ 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index 33f6889..e5924b7 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -1,19 +1,22 @@ 'use client'; import React, { useEffect, useState } from 'react'; -import { Box, Divider } from '@mui/material'; +import { Box, CircularProgress, Divider } from '@mui/material'; import JourneyInfo from '@/components/journey/journeyInfo'; import JourneyPath from '@/components/journey/journeyPath'; -import { getJourney, getTrails } from '@/services/studioMaker.service'; +import { addJourneyToUser, getJourney, getJourneysByUser, getTrails } from '@/services/studioMaker.service'; import { Journey } from '@/lib/interfaces/journey.interface'; import { Trail } from '@/lib/interfaces/trails.interface'; import { useParams } from 'next/navigation'; +import { useSession } from 'next-auth/react'; export default function JourneyPage() { const { journeyId } = useParams(); const [journey, setJourney] = useState(null); const [trails, setTrails] = useState([]); const [error, setError] = useState(null); + const [hasJourney, setHasJourney] = useState(false); + const { data: session } = useSession(); useEffect(() => { const fetchJourneyData = async () => { @@ -26,20 +29,34 @@ export default function JourneyPage() { const trailsData = await getTrails({ id, token }); setTrails(trailsData); + + if (session?.user.id) { + const userJourneys = await getJourneysByUser(session.user.id); + const journeyExists = userJourneys.some(j => j._id === id); + setHasJourney(journeyExists); + } } catch (err) { setError('Failed to fetch journey data'); } }; fetchJourneyData(); - }, [journeyId]); + }, [journeyId, session?.user.id]); + + const handleJoin = async () => { + if (session?.user.id) { + const id = Array.isArray(journeyId) ? journeyId[0] : journeyId; + await addJourneyToUser({ userId: session.user.id, journeyId: id }); + setHasJourney(true); + } + }; if (error) { return
{error}
; } if (!journey || !trails.length) { - return
Loading...
; + return ; } return ( @@ -57,6 +74,8 @@ export default function JourneyPage() { title={journey.title} description={journey.description} trailCount={trails.length} + hasJourney={hasJourney} + onJoin={handleJoin} />
diff --git a/src/components/journey/journeyInfo.tsx b/src/components/journey/journeyInfo.tsx index ac7797b..28c828b 100644 --- a/src/components/journey/journeyInfo.tsx +++ b/src/components/journey/journeyInfo.tsx @@ -1,20 +1,32 @@ 'use client'; import React from 'react'; -import { Box, Button, Typography } from '@mui/material'; +import { Box, Typography } from '@mui/material'; import CollectionsBookmarkIcon from '@mui/icons-material/CollectionsBookmark'; import MyButton from '@/components/ui/buttons/myButton.component'; +import { useSession } from 'next-auth/react'; interface JourneyInfoProps { title: string; description: string; trailCount: number; + hasJourney: boolean; + onJoin: () => void; } + const JourneyInfo: React.FC = ({ title, description, trailCount, + hasJourney, + onJoin, }) => { + const { data: session } = useSession(); + + const handleJoinClick = async () => { + await onJoin(); + }; + return ( = ({ boxShadow: '0 4px 8px rgba(0,0,0,0.5)', maxWidth: '650px', }} - ml={20}> + ml={20} + > = ({ Número de trilhas: {trailCount} - - - Ingressar - - + {!hasJourney && ( + + + Ingressar + + + )}
); }; diff --git a/src/services/studioMaker.service.ts b/src/services/studioMaker.service.ts index 3e82cde..acecd56 100644 --- a/src/services/studioMaker.service.ts +++ b/src/services/studioMaker.service.ts @@ -206,3 +206,22 @@ export const getJourney = async (id: string): Promise => { throw error; } }; + +export const addJourneyToUser = async ({ + userId, + journeyId, +}: { + userId: string; + journeyId: string; +}) => { + try { + const response = await studioMakerApi.patch(`/journeys/${userId}/add-journey`, { + journeyId, + }); + console.log('Journey added to user'); + return response.data; + } catch (error) { + console.error('Failed to add journey to user:', error); + throw error; + } +}; \ No newline at end of file From 4be849e9581b6480138b5b3c5bbaf3779cc27a43 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Mon, 26 Aug 2024 17:33:39 -0300 Subject: [PATCH 07/24] feat(#78): correcao na declaracao da funcao chamada no onclick --- src/components/journey/journeyInfo.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/journey/journeyInfo.tsx b/src/components/journey/journeyInfo.tsx index 28c828b..c6c356a 100644 --- a/src/components/journey/journeyInfo.tsx +++ b/src/components/journey/journeyInfo.tsx @@ -21,10 +21,9 @@ const JourneyInfo: React.FC = ({ hasJourney, onJoin, }) => { - const { data: session } = useSession(); - const handleJoinClick = async () => { - await onJoin(); + const handleJoinClick = () => { + onJoin(); }; return ( From 868a2210d57de414b163bcafbe6d54d426c69b97 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Aug 2024 20:55:53 -0300 Subject: [PATCH 08/24] =?UTF-8?q?feat(#78):=20Alterando=20conex=C3=A3o=20c?= =?UTF-8?q?om=20o=20UserService:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrigindo conexão com o UserService --- src/app/journey-page/[journeyId]/page.tsx | 13 +++++--- src/services/user.service.ts | 37 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index e5924b7..991c73a 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -9,6 +9,7 @@ import { Journey } from '@/lib/interfaces/journey.interface'; import { Trail } from '@/lib/interfaces/trails.interface'; import { useParams } from 'next/navigation'; import { useSession } from 'next-auth/react'; +import { subscribeJourney, getSubscribedJourneys } from '@/services/user.service'; export default function JourneyPage() { const { journeyId } = useParams(); @@ -31,9 +32,10 @@ export default function JourneyPage() { setTrails(trailsData); if (session?.user.id) { - const userJourneys = await getJourneysByUser(session.user.id); - const journeyExists = userJourneys.some(j => j._id === id); - setHasJourney(journeyExists); + const userJourneys = await getSubscribedJourneys(session?.user.id); + console.log(userJourneys); + const filteredJourneys = userJourneys.filter(j => j._id === id); + setHasJourney(filteredJourneys.length > 0); } } catch (err) { setError('Failed to fetch journey data'); @@ -46,9 +48,12 @@ export default function JourneyPage() { const handleJoin = async () => { if (session?.user.id) { const id = Array.isArray(journeyId) ? journeyId[0] : journeyId; - await addJourneyToUser({ userId: session.user.id, journeyId: id }); + console.log(session?.user.accessToken); + await subscribeJourney({ userId: session.user.id, journeyId: id, accessToken: session?.user.accessToken}); setHasJourney(true); } + + }; if (error) { diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 9208791..6432dc1 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -1,6 +1,8 @@ 'use server'; import { userApi } from '@/services/apis.service'; +import { Journey } from '@/lib/interfaces/journey.interface'; +import { string } from 'zod'; export const createUser = async (data: any) => { console.log(data); @@ -90,4 +92,39 @@ export const resetPassword = async (data: any) => { } catch (error) { throw error; } + }; + + export const subscribeJourney = async ({ + userId, + journeyId, + accessToken, + }:{ + userId: string; + journeyId: string; + accessToken: string; + }) => { + try { + const response = await userApi.post(`/users/${userId}/subscribe/${journeyId}`, + {}, + { + headers: { + Authorization: `Bearer ${accessToken}` + } + }); + return response.data; + } catch (err) { + console.log("Failed to subscribe to journey: ", err); + throw err; + } + }; + + export const getSubscribedJourneys = async (userId: string) => { + try { + const response = await userApi.get(`/users/${userId}/subscribedJourneys`); + return response.data; + } catch(error) { + console.log("Error fetched journeys"); + throw error; + } + }; From d4c19fbd810c892b8747707747e335688651ea1f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Aug 2024 21:14:18 -0300 Subject: [PATCH 09/24] =?UTF-8?q?feat(#78):=20Bot=C3=A3o=20de=20progresso?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arrumando a lógica de ingressão de trilha de cada usuário Co-authored-by: Neoprot Co-authored-by: joaobisi Co-authored-by: Joa0V --- src/app/journey-page/[journeyId]/page.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index 991c73a..e41167c 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -31,11 +31,16 @@ export default function JourneyPage() { const trailsData = await getTrails({ id, token }); setTrails(trailsData); - if (session?.user.id) { - const userJourneys = await getSubscribedJourneys(session?.user.id); - console.log(userJourneys); - const filteredJourneys = userJourneys.filter(j => j._id === id); - setHasJourney(filteredJourneys.length > 0); + if (session?.user?.id) { + const userJourneys = await getSubscribedJourneys(session.user.id); + console.log('User journeys: ', userJourneys); + let isSubscribed = false; + userJourneys.forEach(journeyId => { + if(journeyId === id) { + isSubscribed = true; + } + }); + setHasJourney(isSubscribed); } } catch (err) { setError('Failed to fetch journey data'); @@ -43,7 +48,7 @@ export default function JourneyPage() { }; fetchJourneyData(); - }, [journeyId, session?.user.id]); + }, [journeyId, session?.user?.id]); const handleJoin = async () => { if (session?.user.id) { From 26d4c2b08270fa80196c9eca4819dddae5f85736 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Tue, 27 Aug 2024 12:42:11 -0300 Subject: [PATCH 10/24] feat(#78): corrigindo erro de tipagema e importacao --- src/app/journey-page/[journeyId]/page.tsx | 3 +-- src/app/studio/[trailId]/page.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index 991c73a..1d77a84 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -33,8 +33,7 @@ export default function JourneyPage() { if (session?.user.id) { const userJourneys = await getSubscribedJourneys(session?.user.id); - console.log(userJourneys); - const filteredJourneys = userJourneys.filter(j => j._id === id); + const filteredJourneys = userJourneys.filter((j: { _id: string; }) => j._id === id); setHasJourney(filteredJourneys.length > 0); } } catch (err) { diff --git a/src/app/studio/[trailId]/page.tsx b/src/app/studio/[trailId]/page.tsx index 5c151ca..4f9bae4 100644 --- a/src/app/studio/[trailId]/page.tsx +++ b/src/app/studio/[trailId]/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import MarkdownPage from '@/app/components/studio/MarkdownPage'; +import MarkdownPage from '@/components/studio/MarkdownPage'; import { useParams } from 'next/navigation'; export default function StudioPage() { const { trailId } = useParams(); From 1cf54d3220ca73c292eee1a085b9ffe549ebeeda Mon Sep 17 00:00:00 2001 From: joaobisi Date: Tue, 27 Aug 2024 22:27:31 -0300 Subject: [PATCH 11/24] feat(#78): dando forma ao grafo de trilhas --- src/components/journey/journeyPath.tsx | 64 ++++++++++++++++---------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/components/journey/journeyPath.tsx b/src/components/journey/journeyPath.tsx index 3de9178..c9742e1 100644 --- a/src/components/journey/journeyPath.tsx +++ b/src/components/journey/journeyPath.tsx @@ -8,6 +8,8 @@ interface JourneyPathProps { const JourneyPath: React.FC = ({ trails }) => { const nodeSpacing = 120; + const nodeSize = 80; + const zigzagOffset = 100; return ( = ({ trails }) => { pl={2} sx={{ position: 'relative', - height: '500px', + height: `${trails.length * nodeSpacing + nodeSize}px`, + backgroundColor: '#f0f0f0', }} > - {trails.map((trail, index) => ( - - ))} + {trails.map((trail, index) => { + const isLeft = index % 2 === 0; + const offsetX = isLeft ? -zigzagOffset : zigzagOffset; + + return ( + + ); + })} ); }; From e9681b5356adbddb384ebae67f8baa0ef3190978 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Tue, 27 Aug 2024 23:02:23 -0300 Subject: [PATCH 12/24] feat(#78): adicionando conexao entre as trilhas no grafo Co-authored-by: Joa0V Co-authored-by: Nanashii76 --- src/components/journey/journeyPath.tsx | 58 +++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/components/journey/journeyPath.tsx b/src/components/journey/journeyPath.tsx index c9742e1..38733e2 100644 --- a/src/components/journey/journeyPath.tsx +++ b/src/components/journey/journeyPath.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import { Box, Button } from '@mui/material'; import { Trail } from '@/lib/interfaces/trails.interface'; @@ -9,7 +9,47 @@ interface JourneyPathProps { const JourneyPath: React.FC = ({ trails }) => { const nodeSpacing = 120; const nodeSize = 80; - const zigzagOffset = 100; + const zigzagOffset = 100; + + const svgRef = useRef(null); + + useEffect(() => { + if (!svgRef.current) return; + const svg = svgRef.current; + const svgNS = "http://www.w3.org/2000/svg"; + const svgWidth = svg.clientWidth; + const svgHeight = svg.clientHeight; + + // Clear previous lines + while (svg.firstChild) { + svg.removeChild(svg.firstChild); + } + + // Draw lines connecting nodes + trails.forEach((_, index) => { + if (index < trails.length - 1) { + const isLeft1 = index % 2 === 0; + const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; + const top1 = 50 + index * nodeSpacing + nodeSize / 2; + const left1 = svgWidth / 2 + offsetX1; + + const isLeft2 = (index + 1) % 2 === 0; + const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; + const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; + const left2 = svgWidth / 2 + offsetX2; + + const line = document.createElementNS(svgNS, "line"); + line.setAttribute("x1", `${left1}`); + line.setAttribute("y1", `${top1}`); + line.setAttribute("x2", `${left2}`); + line.setAttribute("y2", `${top2}`); + line.setAttribute("stroke", "silver"); + line.setAttribute("stroke-width", "20"); + + svg.appendChild(line); + } + }); + }, [trails]); return ( = ({ trails }) => { ); })} + + {/* SVG for Lines */} + ); }; From 0f795b82258884a220c791de377cdf6e86b2a505 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Aug 2024 23:30:14 -0300 Subject: [PATCH 13/24] =?UTF-8?q?feat(#78):=20P=C3=A1gina=20de=20visualiza?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20trilhas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionando caminho ao grafo de trilha e rotacionando o botão Co-authored-by: joaobisi Co-authored-by: Joa0V --- src/app/journey/[...id]/page.tsx | 2 +- src/components/journey/journeyPath.tsx | 95 ++++++++++++++++---------- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/app/journey/[...id]/page.tsx b/src/app/journey/[...id]/page.tsx index 54369ae..44204c4 100644 --- a/src/app/journey/[...id]/page.tsx +++ b/src/app/journey/[...id]/page.tsx @@ -27,7 +27,7 @@ export default function ManageTrack({ params }: { params: { id: string } }) { isLoading, error, } = useQuery({ - queryKey: ['trails'], + queryKey: ['trails', params.id], queryFn: fetchTrails, }); diff --git a/src/components/journey/journeyPath.tsx b/src/components/journey/journeyPath.tsx index 38733e2..444986c 100644 --- a/src/components/journey/journeyPath.tsx +++ b/src/components/journey/journeyPath.tsx @@ -1,3 +1,4 @@ +'use client' import React, { useEffect, useRef } from 'react'; import { Box, Button } from '@mui/material'; import { Trail } from '@/lib/interfaces/trails.interface'; @@ -12,47 +13,63 @@ const JourneyPath: React.FC = ({ trails }) => { const zigzagOffset = 100; const svgRef = useRef(null); + const containerRef = useRef(null); useEffect(() => { - if (!svgRef.current) return; - const svg = svgRef.current; - const svgNS = "http://www.w3.org/2000/svg"; - const svgWidth = svg.clientWidth; - const svgHeight = svg.clientHeight; - - // Clear previous lines - while (svg.firstChild) { - svg.removeChild(svg.firstChild); - } + const drawLines = () => { + if (!svgRef.current || !containerRef.current) return; + + const svg = svgRef.current; + const svgNS = "http://www.w3.org/2000/svg"; + const container = containerRef.current; + const svgWidth = container.clientWidth; + const svgHeight = container.clientHeight; - // Draw lines connecting nodes - trails.forEach((_, index) => { - if (index < trails.length - 1) { - const isLeft1 = index % 2 === 0; - const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; - const top1 = 50 + index * nodeSpacing + nodeSize / 2; - const left1 = svgWidth / 2 + offsetX1; - - const isLeft2 = (index + 1) % 2 === 0; - const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; - const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; - const left2 = svgWidth / 2 + offsetX2; - - const line = document.createElementNS(svgNS, "line"); - line.setAttribute("x1", `${left1}`); - line.setAttribute("y1", `${top1}`); - line.setAttribute("x2", `${left2}`); - line.setAttribute("y2", `${top2}`); - line.setAttribute("stroke", "silver"); - line.setAttribute("stroke-width", "20"); - - svg.appendChild(line); + while (svg.firstChild) { + svg.removeChild(svg.firstChild); } - }); + + trails.forEach((_, index) => { + if (index < trails.length - 1) { + const isLeft1 = index % 2 === 0; + const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; + const top1 = 50 + index * nodeSpacing + nodeSize / 2; + const left1 = svgWidth / 2 + offsetX1; + + const isLeft2 = (index + 1) % 2 === 0; + const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; + const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; + const left2 = svgWidth / 2 + offsetX2; + + const line = document.createElementNS(svgNS, "line"); + line.setAttribute("x1", `${left1}`); + line.setAttribute("y1", `${top1}`); + line.setAttribute("x2", `${left2}`); + line.setAttribute("y2", `${top2}`); + line.setAttribute("stroke", "silver"); + line.setAttribute("stroke-width", "20"); + + svg.appendChild(line); + } + }); + }; + + drawLines(); + + + const resizeObserver = new ResizeObserver(drawLines); + if (containerRef.current) { + resizeObserver.observe(containerRef.current); + } + + return () => { + resizeObserver.disconnect(); + }; }, [trails]); return ( = ({ trails }) => { borderRadius: '10px', boxShadow: '0 4px 8px rgba(0,0,0,0.3)', zIndex: 1, + transform: 'translateX(-50%) rotate(45deg)', color: 'black', '&:hover': { backgroundColor: 'gray', }, - transform: 'translateX(-50%)', + '& > .content': { + transform: 'rotate(-45deg)', + }, display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -92,12 +112,13 @@ const JourneyPath: React.FC = ({ trails }) => { }} onClick={() => console.log(`Clicked on trail: ${trail.name}`)} > + {trail.name} + ); })} - {/* SVG for Lines */} = ({ trails }) => { width: '100%', height: '100%', zIndex: 0, - pointerEvents: 'none', // Make sure SVG does not interfere with button clicks + pointerEvents: 'none', }} /> ); }; -export default JourneyPath; +export default JourneyPath; \ No newline at end of file From 6b74fd0bcb0532be74c4ba889a474e6872ee49df Mon Sep 17 00:00:00 2001 From: Joa0V Date: Tue, 27 Aug 2024 23:39:54 -0300 Subject: [PATCH 14/24] feat(#78):adicionando caso em que nao ha trilhas na jornada na journey-page Co-authored-by: joaobisi Co-authored-by: Nanashii76 --- src/app/journey-page/[journeyId]/page.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index d2b942b..73ff4b8 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -1,7 +1,7 @@ 'use client'; import React, { useEffect, useState } from 'react'; -import { Box, CircularProgress, Divider } from '@mui/material'; +import { Box, CircularProgress, Divider, Typography } from '@mui/material'; import JourneyInfo from '@/components/journey/journeyInfo'; import JourneyPath from '@/components/journey/journeyPath'; import { addJourneyToUser, getJourney, getJourneysByUser, getTrails } from '@/services/studioMaker.service'; @@ -65,7 +65,7 @@ export default function JourneyPage() { return
{error}
; } - if (!journey || !trails.length) { + if (!journey) { return ; } @@ -90,8 +90,14 @@ export default function JourneyPage() {
- - + {!trails.length ? ( + Ainda não há trilhas nessa jornada + ) : ( + + + + )}
); } From c04815874230dcfdaf7f7ae0ac2bc695e882d118 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Wed, 28 Aug 2024 00:45:25 -0300 Subject: [PATCH 15/24] feat(#78): movendo nome da trilha Co-authored-by: Joa0V Co-authored-by: Nanashii76 --- src/components/journey/journeyPath.tsx | 179 ++++++++++++------------- 1 file changed, 88 insertions(+), 91 deletions(-) diff --git a/src/components/journey/journeyPath.tsx b/src/components/journey/journeyPath.tsx index 444986c..cf57c77 100644 --- a/src/components/journey/journeyPath.tsx +++ b/src/components/journey/journeyPath.tsx @@ -1,6 +1,5 @@ -'use client' import React, { useEffect, useRef } from 'react'; -import { Box, Button } from '@mui/material'; +import { Box, Button, Typography } from '@mui/material'; import { Trail } from '@/lib/interfaces/trails.interface'; interface JourneyPathProps { @@ -13,126 +12,124 @@ const JourneyPath: React.FC = ({ trails }) => { const zigzagOffset = 100; const svgRef = useRef(null); - const containerRef = useRef(null); useEffect(() => { - const drawLines = () => { - if (!svgRef.current || !containerRef.current) return; - - const svg = svgRef.current; - const svgNS = "http://www.w3.org/2000/svg"; - const container = containerRef.current; - const svgWidth = container.clientWidth; - const svgHeight = container.clientHeight; - - while (svg.firstChild) { - svg.removeChild(svg.firstChild); - } - - trails.forEach((_, index) => { - if (index < trails.length - 1) { - const isLeft1 = index % 2 === 0; - const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; - const top1 = 50 + index * nodeSpacing + nodeSize / 2; - const left1 = svgWidth / 2 + offsetX1; - - const isLeft2 = (index + 1) % 2 === 0; - const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; - const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; - const left2 = svgWidth / 2 + offsetX2; - - const line = document.createElementNS(svgNS, "line"); - line.setAttribute("x1", `${left1}`); - line.setAttribute("y1", `${top1}`); - line.setAttribute("x2", `${left2}`); - line.setAttribute("y2", `${top2}`); - line.setAttribute("stroke", "silver"); - line.setAttribute("stroke-width", "20"); - - svg.appendChild(line); - } - }); - }; - - drawLines(); - - - const resizeObserver = new ResizeObserver(drawLines); - if (containerRef.current) { - resizeObserver.observe(containerRef.current); + if (!svgRef.current) return; + const svg = svgRef.current; + const svgNS = "http://www.w3.org/2000/svg"; + const svgWidth = svg.clientWidth; + const svgHeight = svg.clientHeight; + + while (svg.firstChild) { + svg.removeChild(svg.firstChild); } - return () => { - resizeObserver.disconnect(); - }; + trails.forEach((_, index) => { + if (index < trails.length - 1) { + const isLeft1 = index % 2 === 0; + const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; + const top1 = 50 + index * nodeSpacing + nodeSize / 2; + const left1 = svgWidth / 2 + offsetX1; + + const isLeft2 = (index + 1) % 2 === 0; + const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; + const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; + const left2 = svgWidth / 2 + offsetX2; + + const line = document.createElementNS(svgNS, "line"); + line.setAttribute("x1", `${left1}`); + line.setAttribute("y1", `${top1}`); + line.setAttribute("x2", `${left2}`); + line.setAttribute("y2", `${top2}`); + line.setAttribute("stroke", "silver"); + line.setAttribute("stroke-width", "20"); + + svg.appendChild(line); + } + }); }, [trails]); return ( + {trails.map((trail, index) => { const isLeft = index % 2 === 0; const offsetX = isLeft ? -zigzagOffset : zigzagOffset; return ( - + + + {trail.name} + + ); })} - -
); }; -export default JourneyPath; \ No newline at end of file +export default JourneyPath; From 4cf31432dcb4b4662b19f4a05ded011140c89e68 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Wed, 28 Aug 2024 15:21:34 -0300 Subject: [PATCH 16/24] feat(#78): Adicionando teste para a pagina de jornada Co-authored-by: Joa0V Co-authored-by: Nanashii76 --- .../journey-page/[journeyId]/page.test.tsx | 71 +++++++++++++++++++ test/app/studio/[trailId]/page.test.tsx | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/app/journey-page/[journeyId]/page.test.tsx diff --git a/test/app/journey-page/[journeyId]/page.test.tsx b/test/app/journey-page/[journeyId]/page.test.tsx new file mode 100644 index 0000000..4883631 --- /dev/null +++ b/test/app/journey-page/[journeyId]/page.test.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import JourneyPage from '@/app/journey-page/[journeyId]/page'; +import { getJourney, getTrails } from '@/services/studioMaker.service'; +import { getSubscribedJourneys } from '@/services/user.service'; +import { useSession } from 'next-auth/react'; +import { useParams } from 'next/navigation'; +import '@testing-library/jest-dom'; + +jest.mock('@/services/studioMaker.service'); +jest.mock('@/services/user.service'); +jest.mock('next-auth/react'); +jest.mock('next/navigation', () => ({ + useParams: jest.fn(), +})); + +describe('JourneyPage', () => { + const journeyMock = { _id: '1', title: 'Journey 1', description: 'Description of Journey 1', trails: ['1'] }; + const trailsMock = [{ _id: '1', name: 'Trail 1', journey: '1' }]; + const sessionMock = { data: { user: { _id: 'user123', accessToken: 'token123' } } }; + + beforeEach(() => { + (getJourney as jest.Mock).mockResolvedValue(journeyMock); + (getTrails as jest.Mock).mockResolvedValue(trailsMock); + (getSubscribedJourneys as jest.Mock).mockResolvedValue(['1']); + (useSession as jest.Mock).mockReturnValue(sessionMock); + (useParams as jest.Mock).mockReturnValue({ journeyId: '1' }); + }); + + it('deve exibir um spinner enquanto os dados estão carregando', async () => { + render(); + + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + + await waitFor(() => { + expect(getJourney).toHaveBeenCalledTimes(1); + expect(getTrails).toHaveBeenCalledTimes(1); + }); + }); + + it('deve exibir um erro se falhar ao carregar os dados', async () => { + (getJourney as jest.Mock).mockRejectedValue(new Error('Failed to fetch journey data')); + + render(); + + await waitFor(() => { + expect(screen.getByText('Failed to fetch journey data')).toBeInTheDocument(); + }); + }); + + it('deve exibir os dados da jornada e das trilhas quando carregados com sucesso', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText(journeyMock.title)).toBeInTheDocument(); + expect(screen.getByText(journeyMock.description)).toBeInTheDocument(); + + expect(screen.getByText((content, element) => content.includes('Trail 1'))).toBeInTheDocument(); + }); + }); + + it('deve exibir uma mensagem quando não há trilhas na jornada', async () => { + (getTrails as jest.Mock).mockResolvedValue([]); + + render(); + + await waitFor(() => { + expect(screen.getByText('Ainda não há trilhas nessa jornada')).toBeInTheDocument(); + }); + }); +}); diff --git a/test/app/studio/[trailId]/page.test.tsx b/test/app/studio/[trailId]/page.test.tsx index fc257ea..1147db8 100644 --- a/test/app/studio/[trailId]/page.test.tsx +++ b/test/app/studio/[trailId]/page.test.tsx @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'; import StudioPage from '@/app/studio/[trailId]/page'; import { useParams } from 'next/navigation'; -jest.mock('@/app/components/studio/MarkdownPage', () => ({ +jest.mock('@/components/studio/MarkdownPage', () => ({ __esModule: true, default: jest.fn(() =>
Mock MarkdownPage
), })); From 027a5eb0e3b99d4805be234678327ff6a2a46755 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Aug 2024 20:44:39 -0300 Subject: [PATCH 17/24] =?UTF-8?q?feat(#78):=20Arrumando=20p=C3=A1gina=20de?= =?UTF-8?q?=20progresso=20de=20trilha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consertando a resposividade do grafo e rodando os teste Co-authored-by: joaobisi Co-authored-by: Joa0V --- src/app/journey-page/[journeyId]/page.tsx | 2 +- src/components/journey/journeyPath.tsx | 75 +++++++++++-------- .../journey-page/[journeyId]/page.test.tsx | 8 ++ 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/app/journey-page/[journeyId]/page.tsx b/src/app/journey-page/[journeyId]/page.tsx index 73ff4b8..43b5b4e 100644 --- a/src/app/journey-page/[journeyId]/page.tsx +++ b/src/app/journey-page/[journeyId]/page.tsx @@ -76,7 +76,7 @@ export default function JourneyPage() { alignItems: 'center', justifyContent: 'center', backgroundColor: '#f1f1f1', - height: '100vh', + height: 'auto', }} > diff --git a/src/components/journey/journeyPath.tsx b/src/components/journey/journeyPath.tsx index cf57c77..f10909d 100644 --- a/src/components/journey/journeyPath.tsx +++ b/src/components/journey/journeyPath.tsx @@ -12,45 +12,61 @@ const JourneyPath: React.FC = ({ trails }) => { const zigzagOffset = 100; const svgRef = useRef(null); + const containerRef = useRef(null); useEffect(() => { - if (!svgRef.current) return; - const svg = svgRef.current; - const svgNS = "http://www.w3.org/2000/svg"; - const svgWidth = svg.clientWidth; - const svgHeight = svg.clientHeight; + const drawLines = () => { + if (!svgRef.current || !containerRef.current) return; + const svg = svgRef.current; + const svgNS = "http://www.w3.org/2000/svg"; + const svgWidth = svg.clientWidth; + const container = containerRef.current; + const svgHeight = svg.clientHeight; - while (svg.firstChild) { - svg.removeChild(svg.firstChild); - } + while (svg.firstChild) { + svg.removeChild(svg.firstChild); + } - trails.forEach((_, index) => { - if (index < trails.length - 1) { - const isLeft1 = index % 2 === 0; - const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; - const top1 = 50 + index * nodeSpacing + nodeSize / 2; - const left1 = svgWidth / 2 + offsetX1; + trails.forEach((_, index) => { + if (index < trails.length - 1) { + const isLeft1 = index % 2 === 0; + const offsetX1 = isLeft1 ? -zigzagOffset : zigzagOffset; + const top1 = 50 + index * nodeSpacing + nodeSize / 2; + const left1 = svgWidth / 2 + offsetX1; - const isLeft2 = (index + 1) % 2 === 0; - const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; - const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; - const left2 = svgWidth / 2 + offsetX2; + const isLeft2 = (index + 1) % 2 === 0; + const offsetX2 = isLeft2 ? -zigzagOffset : zigzagOffset; + const top2 = 50 + (index + 1) * nodeSpacing + nodeSize / 2; + const left2 = svgWidth / 2 + offsetX2; - const line = document.createElementNS(svgNS, "line"); - line.setAttribute("x1", `${left1}`); - line.setAttribute("y1", `${top1}`); - line.setAttribute("x2", `${left2}`); - line.setAttribute("y2", `${top2}`); - line.setAttribute("stroke", "silver"); - line.setAttribute("stroke-width", "20"); + const line = document.createElementNS(svgNS, "line"); + line.setAttribute("x1", `${left1}`); + line.setAttribute("y1", `${top1}`); + line.setAttribute("x2", `${left2}`); + line.setAttribute("y2", `${top2}`); + line.setAttribute("stroke", "silver"); + line.setAttribute("stroke-width", "20"); - svg.appendChild(line); - } - }); + svg.appendChild(line); + } + }); + }; + + drawLines(); + + const resizeObserver = new ResizeObserver(drawLines); + if (containerRef.current) { + resizeObserver.observe(containerRef.current); + } + + return () => { + resizeObserver.disconnect(); + }; }, [trails]); return ( = ({ trails }) => { transform: 'rotate(45deg)', }} onClick={() => console.log(`Clicked on trail: ${trail.name}`)} - > - + /> ({ useParams: jest.fn(), })); +beforeAll(() => { + global.ResizeObserver = class { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + describe('JourneyPage', () => { const journeyMock = { _id: '1', title: 'Journey 1', description: 'Description of Journey 1', trails: ['1'] }; const trailsMock = [{ _id: '1', name: 'Trail 1', journey: '1' }]; From 2659d4aa29138efaf3713dec7514f938fad21671 Mon Sep 17 00:00:00 2001 From: NATAN Date: Thu, 29 Aug 2024 23:09:49 -0300 Subject: [PATCH 18/24] feat#79: alterando visual home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit modificando home de acordo com os novos protótipos Co-authored-by: Neoprot --- src/components/home/JourneyCard.tsx | 27 +++ src/components/home/StartPointsCard.tsx | 113 +++++++++++++ src/components/home/homeJourneyCard.tsx | 41 ----- src/components/home/homePage.tsx | 210 ++++++++++++++++++------ 4 files changed, 303 insertions(+), 88 deletions(-) create mode 100644 src/components/home/JourneyCard.tsx create mode 100644 src/components/home/StartPointsCard.tsx delete mode 100644 src/components/home/homeJourneyCard.tsx diff --git a/src/components/home/JourneyCard.tsx b/src/components/home/JourneyCard.tsx new file mode 100644 index 0000000..e9c00d2 --- /dev/null +++ b/src/components/home/JourneyCard.tsx @@ -0,0 +1,27 @@ +'use client'; +import React from 'react'; +import Image from 'next/image'; +import 'react-multi-carousel/lib/styles.css'; + +interface JourneyCardProps { + title: string; + image: string; + Id: string; +} + +const JourneyCard: React.FC = ({ title, image, Id }) => { + return ( +
+ {title} +

{title}

+
+ ); +}; + +export default JourneyCard; diff --git a/src/components/home/StartPointsCard.tsx b/src/components/home/StartPointsCard.tsx new file mode 100644 index 0000000..11985d5 --- /dev/null +++ b/src/components/home/StartPointsCard.tsx @@ -0,0 +1,113 @@ +'use client'; +import React from 'react'; +import Carousel from 'react-multi-carousel'; +import Image from 'next/image'; +import { ChevronUp, ChevronDown } from 'lucide-react'; +import Foto from '@/public/calculus-logo.svg'; + + +interface StartCardProps { + title: string; + image: string; + description?: string; + Id: string; +} + + +//puxar as jornadas com base no Id passado +const jornadasTest = [ + { + img: Foto, + title: 'Jornada 1', + }, + { + img: Foto, + title: 'Jornada 2', + }, + { + img: Foto, + title: 'Jornada 3', + }, + { + img: Foto, + title: 'Jornada 4', + }, + { + img: Foto, + title: 'Jornada 5', + }, + { + img: Foto, + title: 'Jornada 6', + }, + { + img: Foto, + title: 'Jornada 7', + }, + { + img: Foto, + title: 'Jornada 8', + }, + { + img: Foto, + title: 'Jornada 9', + }, +] + +const StartCard: React.FC = ({ title, image, description, Id }) => { + const [isOpen, setIsOpen] = React.useState(false); + + const responsive = { + superLargeDesktop: { + // the naming can be any, depends on you. + breakpoint: { max: 4000, min: 3000 }, + items: 5 + }, + desktop: { + breakpoint: { max: 3000, min: 1024 }, + items: 5 + }, + tablet: { + breakpoint: { max: 1024, min: 464 }, + items: 3 + }, + mobile: { + breakpoint: { max: 464, min: 0 }, + items: 2 + } + }; + + return ( +
+
{setIsOpen(!isOpen)}} className="flex gap-3 py-5 px-4 hover:bg-[#ececec] rounded-xl cursor-pointer items-center"> + {title} +

{title}

+

{description}

+
+ {isOpen ? : } +
+
+ {isOpen && ( +
+ + {jornadasTest.map((jornada, index) => ( +
+
+ {jornada.title} +
+

{jornada.title}

+ + {index < jornadasTest.length - 1 && ( +
+ )} +
+ ))} +
+
+ )} +
+
+ ); +}; + +export default StartCard; diff --git a/src/components/home/homeJourneyCard.tsx b/src/components/home/homeJourneyCard.tsx deleted file mode 100644 index 30d3012..0000000 --- a/src/components/home/homeJourneyCard.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client'; -import React from 'react'; -import Image from 'next/image'; -import MyButton from '@/components/ui/buttons/myButton.component'; -interface JourneyCardProps { - type: 'emAndamento' | 'geral'; - title: string; - image: string; - description?: string; - URL?: string; -} - -const JourneyCard: React.FC = ({ - type, - title, - image, - description, - URL, -}) => { - if (type === 'emAndamento') { - return ( -
- {title} -

{title}

-
- ); - } else { - return ( -
- {title} -

{title}

-

{description}

- - VER TRILHAS - -
- ); - } -}; - -export default JourneyCard; diff --git a/src/components/home/homePage.tsx b/src/components/home/homePage.tsx index a67b497..5c4acb8 100644 --- a/src/components/home/homePage.tsx +++ b/src/components/home/homePage.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react'; import Carousel from 'react-multi-carousel'; import 'react-multi-carousel/lib/styles.css'; import Foto from '@/public/calculus-logo.svg'; -import JourneyCard from '@/components/home/homeJourneyCard'; +import JourneyCard from '@/components/home/JourneyCard'; +import StartCard from '@/components/home/StartPointsCard'; import JourneyService from './service/home.services'; import SearchBar from './SearchBar'; import { useSession } from 'next-auth/react'; @@ -13,42 +14,105 @@ const HomePrincipalPage = () => { const [userJourneys, setUserJourneys] = useState([]); const [allJourneys, setAllJourneys] = useState([]); - useEffect(() => { - try { - const fetchJourneys = async () => { - const { fetchUserJourneys, fetchJourneyById } = JourneyService(); - const journeyIds = await fetchUserJourneys(session); + // useEffect(() => { + // try { + // const fetchJourneys = async () => { + // const { fetchUserJourneys, fetchJourneyById } = JourneyService(); + // const journeyIds = await fetchUserJourneys(session); - const journeysDetails = await Promise.all( - journeyIds.map(async (id: string) => await fetchJourneyById(id)), - ); + // const journeysDetails = await Promise.all( + // journeyIds.map(async (id: string) => await fetchJourneyById(id)), + // ); - setUserJourneys(journeysDetails.filter((j) => j !== null)); // Filtrar jornadas que foram encontradas - }; + // setUserJourneys(journeysDetails.filter((j) => j !== null)); // Filtrar jornadas que foram encontradas + // }; - fetchJourneys(); - } catch (error) { - console.log(error); - } - }, [session]); + // fetchJourneys(); + // } catch (error) { + // console.log(error); + // } + // }, [session]); - useEffect(() => { - try { - const loadJourneys = async () => { - const { fetchJourneys } = JourneyService(); - const allJourneys = await fetchJourneys(); + // useEffect(() => { + // try { + // const loadJourneys = async () => { + // const { fetchJourneys } = JourneyService(); + // const allJourneys = await fetchJourneys(); - setAllJourneys(allJourneys); - }; - loadJourneys(); - } catch (error) { - console.log(error); - } - }, []); + // setAllJourneys(allJourneys); + // }; + // loadJourneys(); + // } catch (error) { + // console.log(error); + // } + // }, []); + const allStartPointsTest = [ + { + _id: '1', + title: 'Ponto de Partida 1', + description: '', + image: Foto, + }, + { + _id: '2', + title: 'Ponto de Partida 2', + description: 'Descrição da Ponto de Partida 2', + image: Foto, + }, + { + _id: '3', + title: 'Ponto de Partida 3', + description: 'Descrição da Ponto de Partida 3', + image: Foto, + }, + { + _id: '4', + title: 'Ponto de Partida 4', + description: '', + image: Foto, + }, + { + _id: '5', + title: 'Ponto de Partida 5', + description: 'Descrição da Ponto de Partida 5', + image: Foto, + }, + { + _id: '6', + title: 'Ponto de Partida 6', + description: '', + image: Foto, + }, + { + _id: '7', + title: 'Ponto de Partida 7', + description: 'Descrição da Ponto de Partida 7', + image: Foto, + }, + { + _id: '8', + title: 'Ponto de Partida 8', + description: 'Descrição da Ponto de Partida 8', + image: Foto, + }, + { + _id: '9', + title: 'Ponto de Partida 9', + description: 'Descrição da Ponto de Partida 9', + image: Foto, + }, + { + _id: '10', + title: 'Ponto de Partida 10', + description: '', + image: Foto, + }, + ]; + const filteredJourneys = searchQuery.length > 0 - ? allJourneys.filter( + ? allStartPointsTest.filter( (jornada) => jornada.title.toLowerCase().includes(searchQuery.toLowerCase()) || jornada.description @@ -56,7 +120,7 @@ const HomePrincipalPage = () => { .includes(searchQuery.toLowerCase()), ) : []; - + const responsive = { superLargeDesktop: { breakpoint: { max: 4000, min: 3000 }, @@ -78,20 +142,74 @@ const HomePrincipalPage = () => { const handleSearch = (query: string) => { setSearchQuery(query); - } + }; + + const userJourneysTest = [ + { + _id: '1', + title: 'Jornada 1', + image: Foto, + }, + { + _id: '2', + title: 'Jornada 2', + image: Foto, + }, + { + _id: '3', + title: 'Jornada 3', + image: Foto, + }, + { + _id: '4', + title: 'Jornada 4', + image: Foto, + }, + { + _id: '5', + title: 'Jornada 5', + image: Foto, + }, + { + _id: '6', + title: 'Jornada 6', + image: Foto, + }, + { + _id: '7', + title: 'Jornada 7', + image: Foto, + }, + { + _id: '8', + title: 'Jornada 8', + image: Foto, + }, + { + _id: '9', + title: 'Jornada 9', + image: Foto, + }, + { + _id: '10', + title: 'Jornada 10', + image: Foto, + }, + ]; + return ( <>
Em andamento
- {userJourneys.length > 0 ? ( + {userJourneysTest.length > 0 ? ( <> - {userJourneys.map((jornada) => ( + {userJourneysTest.map((jornada) => ( ))} @@ -107,32 +225,30 @@ const HomePrincipalPage = () => { <>
-

Jornadas

+

Pontos de partida

{searchQuery.length > 0 ? (
{filteredJourneys.map((jornada) => ( - + ))}
) : (
- {allJourneys.map((jornada) => ( - ( + ))}
From 21a3df68f7a9c5514a847ca99a90c71cb0cf479e Mon Sep 17 00:00:00 2001 From: NATAN Date: Fri, 30 Aug 2024 14:50:36 -0300 Subject: [PATCH 19/24] feat#79: puxando pontos de partida puxando pontos de partidas e suas jornadas Co-authored-by: Neoprot --- src/components/home/StartPointsCard.tsx | 130 +++++++++-------- src/components/home/homePage.tsx | 132 ++++++------------ src/components/home/service/home.services.tsx | 22 +++ 3 files changed, 125 insertions(+), 159 deletions(-) diff --git a/src/components/home/StartPointsCard.tsx b/src/components/home/StartPointsCard.tsx index 11985d5..9fdae11 100644 --- a/src/components/home/StartPointsCard.tsx +++ b/src/components/home/StartPointsCard.tsx @@ -1,10 +1,10 @@ 'use client'; -import React from 'react'; +import React, { useState, useEffect} from 'react'; import Carousel from 'react-multi-carousel'; import Image from 'next/image'; -import { ChevronUp, ChevronDown } from 'lucide-react'; +import { ChevronUp, ChevronDown } from 'lucide-react'; import Foto from '@/public/calculus-logo.svg'; - +import JourneyService from './service/home.services'; interface StartCardProps { title: string; @@ -13,99 +13,95 @@ interface StartCardProps { Id: string; } +const StartCard: React.FC = ({ + title, + image, + description, + Id, +}) => { + const [isOpen, setIsOpen] = React.useState(false); + const [journeys, setJourneys] = useState([]); -//puxar as jornadas com base no Id passado -const jornadasTest = [ - { - img: Foto, - title: 'Jornada 1', - }, - { - img: Foto, - title: 'Jornada 2', - }, - { - img: Foto, - title: 'Jornada 3', - }, - { - img: Foto, - title: 'Jornada 4', - }, - { - img: Foto, - title: 'Jornada 5', - }, - { - img: Foto, - title: 'Jornada 6', - }, - { - img: Foto, - title: 'Jornada 7', - }, - { - img: Foto, - title: 'Jornada 8', - }, - { - img: Foto, - title: 'Jornada 9', - }, -] + useEffect(() => { + try { + const loadJourneys = async () => { + const { fetchJourneybyPoint, fetchJourneyById } = JourneyService(); + const journeysId = await fetchJourneybyPoint(Id); + const j: any[] = []; + journeysId.map(async (journeyId: any) => { + j.push(await fetchJourneyById(journeyId)); + }) -const StartCard: React.FC = ({ title, image, description, Id }) => { - const [isOpen, setIsOpen] = React.useState(false); + setJourneys(j); + }; + loadJourneys(); + } catch (error) { + + } + }, []); const responsive = { superLargeDesktop: { // the naming can be any, depends on you. breakpoint: { max: 4000, min: 3000 }, - items: 5 + items: 5, }, desktop: { breakpoint: { max: 3000, min: 1024 }, - items: 5 + items: 5, }, tablet: { breakpoint: { max: 1024, min: 464 }, - items: 3 + items: 3, }, mobile: { breakpoint: { max: 464, min: 0 }, - items: 2 - } + items: 2, + }, }; return (
-
{setIsOpen(!isOpen)}} className="flex gap-3 py-5 px-4 hover:bg-[#ececec] rounded-xl cursor-pointer items-center"> +
{ + setIsOpen(!isOpen); + }} + className="flex gap-3 py-5 px-4 hover:bg-[#ececec] rounded-xl cursor-pointer items-center" + > {title}

{title}

{description}

-
- {isOpen ? : } +
+ {isOpen ? : }
{isOpen && ( -
- - {jornadasTest.map((jornada, index) => ( -
-
- {jornada.title} -
-

{jornada.title}

+
+ + {journeys.map((jornada, index) => ( +
+
+ {jornada.title} +
+

{jornada.title}

- {index < jornadasTest.length - 1 && ( -
- )} -
- ))} -
+ {index < journeys.length - 1 && ( +
+ )} +
+ ))} +
)} -
+
); }; diff --git a/src/components/home/homePage.tsx b/src/components/home/homePage.tsx index 5c4acb8..1194532 100644 --- a/src/components/home/homePage.tsx +++ b/src/components/home/homePage.tsx @@ -12,40 +12,41 @@ const HomePrincipalPage = () => { const { data: session } = useSession(); const [searchQuery, setSearchQuery] = useState(''); const [userJourneys, setUserJourneys] = useState([]); - const [allJourneys, setAllJourneys] = useState([]); + const [allPoints, setAllPoints] = useState([]); - // useEffect(() => { - // try { - // const fetchJourneys = async () => { - // const { fetchUserJourneys, fetchJourneyById } = JourneyService(); - // const journeyIds = await fetchUserJourneys(session); + useEffect(() => { + try { + const fetchJourneys = async () => { + const { fetchUserJourneys, fetchJourneyById } = JourneyService(); + const journeyIds = await fetchUserJourneys(session); - // const journeysDetails = await Promise.all( - // journeyIds.map(async (id: string) => await fetchJourneyById(id)), - // ); + const journeysDetails = await Promise.all( + journeyIds.map(async (id: string) => await fetchJourneyById(id)), + ); - // setUserJourneys(journeysDetails.filter((j) => j !== null)); // Filtrar jornadas que foram encontradas - // }; + setUserJourneys(journeysDetails.filter((j) => j !== null)); + }; - // fetchJourneys(); - // } catch (error) { - // console.log(error); - // } - // }, [session]); + fetchJourneys(); + } catch (error) { + console.log(error); + } + }, [session]); - // useEffect(() => { - // try { - // const loadJourneys = async () => { - // const { fetchJourneys } = JourneyService(); - // const allJourneys = await fetchJourneys(); - // setAllJourneys(allJourneys); - // }; - // loadJourneys(); - // } catch (error) { - // console.log(error); - // } - // }, []); + useEffect(() => { + try { + const loadPoints = async () => { + const { fetchPoints } = JourneyService(); + const allPoints = await fetchPoints(); + + setAllPoints(allPoints); + }; + loadPoints(); + } catch (error) { + console.log(error); + } + }, []); const allStartPointsTest = [ { @@ -110,11 +111,11 @@ const HomePrincipalPage = () => { }, ]; - const filteredJourneys = + const filteredPoints = searchQuery.length > 0 - ? allStartPointsTest.filter( + ? allPoints.filter( (jornada) => - jornada.title.toLowerCase().includes(searchQuery.toLowerCase()) || + jornada.name.toLowerCase().includes(searchQuery.toLowerCase()) || jornada.description .toLowerCase() .includes(searchQuery.toLowerCase()), @@ -144,67 +145,14 @@ const HomePrincipalPage = () => { setSearchQuery(query); }; - const userJourneysTest = [ - { - _id: '1', - title: 'Jornada 1', - image: Foto, - }, - { - _id: '2', - title: 'Jornada 2', - image: Foto, - }, - { - _id: '3', - title: 'Jornada 3', - image: Foto, - }, - { - _id: '4', - title: 'Jornada 4', - image: Foto, - }, - { - _id: '5', - title: 'Jornada 5', - image: Foto, - }, - { - _id: '6', - title: 'Jornada 6', - image: Foto, - }, - { - _id: '7', - title: 'Jornada 7', - image: Foto, - }, - { - _id: '8', - title: 'Jornada 8', - image: Foto, - }, - { - _id: '9', - title: 'Jornada 9', - image: Foto, - }, - { - _id: '10', - title: 'Jornada 10', - image: Foto, - }, - ]; - return ( <>
Em andamento
- {userJourneysTest.length > 0 ? ( + {userJourneys.length > 0 ? ( <> - {userJourneysTest.map((jornada) => ( + {userJourneys.map((jornada) => ( {
{searchQuery.length > 0 ? (
- {filteredJourneys.map((jornada) => ( + {filteredPoints.map((jornada) => ( ))}
) : (
- {allStartPointsTest.map((jornada) => ( + {allPoints.map((jornada) => ( ))}
diff --git a/src/components/home/service/home.services.tsx b/src/components/home/service/home.services.tsx index faba442..068ac56 100644 --- a/src/components/home/service/home.services.tsx +++ b/src/components/home/service/home.services.tsx @@ -36,10 +36,32 @@ const JourneyService = () => { } }; + const fetchPoints = async () =>{ + try { + const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL_STUDIO}/points/`); + return response.data; + } catch (error) { + console.error('Erro ao buscar pontos:', error); + return null; + } + } + + const fetchJourneybyPoint = async (id: string) => { + try { + const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL_STUDIO}/points/${id}/journeys`); + return response.data; + } catch (error) { + console.error(`Erro ao buscar jornada com ID ${id}:`, error); + return null; + } + } + return { fetchUserJourneys, fetchJourneys, fetchJourneyById, + fetchPoints, + fetchJourneybyPoint, }; }; From 1b3e2d1f543d79a0cd5c83c8fd4fc28549628f26 Mon Sep 17 00:00:00 2001 From: Neoprot Date: Fri, 30 Aug 2024 15:22:12 -0300 Subject: [PATCH 20/24] feat(#79):Criando o redirecionamento Criando o redirecionamento ao clickar em uma jornada de um point Co-authored-by: natangoatoso Co-authored-by Nanashii76 --- src/components/home/StartPointsCard.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/home/StartPointsCard.tsx b/src/components/home/StartPointsCard.tsx index 9fdae11..6f2e6aa 100644 --- a/src/components/home/StartPointsCard.tsx +++ b/src/components/home/StartPointsCard.tsx @@ -5,6 +5,7 @@ import Image from 'next/image'; import { ChevronUp, ChevronDown } from 'lucide-react'; import Foto from '@/public/calculus-logo.svg'; import JourneyService from './service/home.services'; +import { useRouter } from 'next/navigation'; interface StartCardProps { title: string; @@ -13,6 +14,8 @@ interface StartCardProps { Id: string; } + + const StartCard: React.FC = ({ title, image, @@ -21,6 +24,11 @@ const StartCard: React.FC = ({ }) => { const [isOpen, setIsOpen] = React.useState(false); const [journeys, setJourneys] = useState([]); + const router = useRouter(); + + const handleOnclick = (id: string) => { + router.push('/journey-page/' + id); + } useEffect(() => { try { @@ -83,7 +91,7 @@ const StartCard: React.FC = ({ key={index} className="h-52 w-58 flex flex-col items-center cursor-pointer " > -
+
handleOnclick(jornada._id)}> {jornada.title} Date: Fri, 30 Aug 2024 15:22:12 -0300 Subject: [PATCH 21/24] feat(#79):Criando o redirecionamento Criando o redirecionamento ao clickar em uma jornada de um point Co-authored-by: natangoatoso Co-authored-by: Nanashii76 --- src/components/home/StartPointsCard.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/home/StartPointsCard.tsx b/src/components/home/StartPointsCard.tsx index 9fdae11..6f2e6aa 100644 --- a/src/components/home/StartPointsCard.tsx +++ b/src/components/home/StartPointsCard.tsx @@ -5,6 +5,7 @@ import Image from 'next/image'; import { ChevronUp, ChevronDown } from 'lucide-react'; import Foto from '@/public/calculus-logo.svg'; import JourneyService from './service/home.services'; +import { useRouter } from 'next/navigation'; interface StartCardProps { title: string; @@ -13,6 +14,8 @@ interface StartCardProps { Id: string; } + + const StartCard: React.FC = ({ title, image, @@ -21,6 +24,11 @@ const StartCard: React.FC = ({ }) => { const [isOpen, setIsOpen] = React.useState(false); const [journeys, setJourneys] = useState([]); + const router = useRouter(); + + const handleOnclick = (id: string) => { + router.push('/journey-page/' + id); + } useEffect(() => { try { @@ -83,7 +91,7 @@ const StartCard: React.FC = ({ key={index} className="h-52 w-58 flex flex-col items-center cursor-pointer " > -
+
handleOnclick(jornada._id)}> {jornada.title} Date: Sun, 1 Sep 2024 23:53:07 -0300 Subject: [PATCH 22/24] feat(#79):adicionando redirecionamento a jornada do JourneyCard --- src/components/home/JourneyCard.tsx | 9 ++++++++- src/components/home/homePage.tsx | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/home/JourneyCard.tsx b/src/components/home/JourneyCard.tsx index e9c00d2..9e919b2 100644 --- a/src/components/home/JourneyCard.tsx +++ b/src/components/home/JourneyCard.tsx @@ -2,6 +2,7 @@ import React from 'react'; import Image from 'next/image'; import 'react-multi-carousel/lib/styles.css'; +import { useRouter } from 'next/navigation'; interface JourneyCardProps { title: string; @@ -10,8 +11,14 @@ interface JourneyCardProps { } const JourneyCard: React.FC = ({ title, image, Id }) => { + + const router = useRouter(); + const handleClick = () => { + router.push('/journey-page/' + Id); + } + return ( -
+
{title} { key={jornada._id} title={jornada.title} image={jornada.image || Foto} - Id="/" + Id={jornada._id} /> ))} From 155aaeebd700b85e990e347cbd02f22d8316ae1d Mon Sep 17 00:00:00 2001 From: NATAN Date: Mon, 2 Sep 2024 14:15:16 -0300 Subject: [PATCH 23/24] feat#79: atualizando testes --- src/components/home/StartPointsCard.tsx | 97 +++++++++++++++---------- test/app/homePage/home.jouney.test.tsx | 40 +++++----- 2 files changed, 80 insertions(+), 57 deletions(-) diff --git a/src/components/home/StartPointsCard.tsx b/src/components/home/StartPointsCard.tsx index 6f2e6aa..184768a 100644 --- a/src/components/home/StartPointsCard.tsx +++ b/src/components/home/StartPointsCard.tsx @@ -1,5 +1,5 @@ 'use client'; -import React, { useState, useEffect} from 'react'; +import React, { useState, useEffect } from 'react'; import Carousel from 'react-multi-carousel'; import Image from 'next/image'; import { ChevronUp, ChevronDown } from 'lucide-react'; @@ -14,8 +14,6 @@ interface StartCardProps { Id: string; } - - const StartCard: React.FC = ({ title, image, @@ -28,25 +26,41 @@ const StartCard: React.FC = ({ const handleOnclick = (id: string) => { router.push('/journey-page/' + id); - } + }; useEffect(() => { - try { - const loadJourneys = async () => { + const loadJourneys = async () => { + try { const { fetchJourneybyPoint, fetchJourneyById } = JourneyService(); const journeysId = await fetchJourneybyPoint(Id); + + if (!journeysId || journeysId.length === 0) { + console.error('No journeys found for this point.'); + return; + } + const j: any[] = []; - journeysId.map(async (journeyId: any) => { - j.push(await fetchJourneyById(journeyId)); - }) + for (const journeyId of journeysId) { + const journey = await fetchJourneyById(journeyId); + if (journey) { + j.push(journey); + } else { + console.error(`Journey with ID ${journeyId} not found.`); + } + } + + if (j.length === 0) { + console.error('No valid journeys found.'); + } setJourneys(j); - }; - loadJourneys(); - } catch (error) { - - } - }, []); + } catch (error) { + console.error('Error loading journeys:', error); + } + }; + + loadJourneys(); + }, [Id]); const responsive = { superLargeDesktop: { @@ -78,35 +92,42 @@ const StartCard: React.FC = ({ > {title}

{title}

-

{description}

+

{description || ""}

{isOpen ? : }
{isOpen && (
- - {journeys.map((jornada, index) => ( -
-
handleOnclick(jornada._id)}> - {jornada.title} -
-

{jornada.title}

+ {journeys.length > 0 ? ( + + {journeys.map((jornada, index) => ( +
+
handleOnclick(jornada._id)} + > + {jornada.title} +
+

{jornada.title}

- {index < journeys.length - 1 && ( -
- )} -
- ))} -
+ {index < journeys.length - 1 && ( +
+ )} +
+ ))} +
+ ) : ( +

No journeys available.

+ )}
)}
@@ -114,4 +135,4 @@ const StartCard: React.FC = ({ ); }; -export default StartCard; +export default StartCard; \ No newline at end of file diff --git a/test/app/homePage/home.jouney.test.tsx b/test/app/homePage/home.jouney.test.tsx index 841f639..93a9d62 100644 --- a/test/app/homePage/home.jouney.test.tsx +++ b/test/app/homePage/home.jouney.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import JourneyCard from '@/components/home/homeJourneyCard'; +import JourneyCard from '@/components/home/JourneyCard'; +import StartCard from '@/components/home/StartPointsCard'; import '@testing-library/jest-dom'; jest.mock('next/image', () => ({ @@ -18,14 +19,20 @@ jest.mock('@/components/ui/buttons/myButton.component', () => ({ ), })); +jest.mock('next/navigation', () => ({ + useRouter: () => ({ + push: jest.fn(), + }), +})); + describe('JourneyCard Component', () => { it('renders emAndamento type correctly', () => { render( + Id="1" + /> ); const title = screen.getByText('Journey in Progress'); @@ -36,46 +43,41 @@ describe('JourneyCard Component', () => { expect(image).toHaveAttribute('src', '/path/to/image.jpg'); }); - it('renders geral type correctly', () => { + it('renders Start Points type correctly', () => { render( - ); const title = screen.getByText('General Journey'); const image = screen.getByAltText('General Journey'); const description = screen.getByText('This is a general journey description.'); - const button = screen.getByText('VER TRILHAS'); expect(title).toBeInTheDocument(); expect(image).toBeInTheDocument(); expect(image).toHaveAttribute('src', '/path/to/general-image.jpg'); expect(description).toBeInTheDocument(); - expect(button).toBeInTheDocument(); }); - it('does not render description when not provided for geral type', () => { + it('does not render description when not provided for Start Point type', () => { render( - ); const title = screen.getByText('General Journey'); const image = screen.getByAltText('General Journey'); const description = screen.queryByText('This is a general journey description.'); - const button = screen.getByText('VER TRILHAS'); expect(title).toBeInTheDocument(); expect(image).toBeInTheDocument(); expect(description).not.toBeInTheDocument(); - expect(button).toBeInTheDocument(); }); }); From a57dbd99423764a538559bb3a43bce3ba0f3eaf8 Mon Sep 17 00:00:00 2001 From: NATAN Date: Mon, 2 Sep 2024 14:44:56 -0300 Subject: [PATCH 24/24] feat#79: atualizando testes --- src/components/home/StartPointsCard.tsx | 1 - src/components/home/service/home.services.tsx | 4 +- test/app/homePage/home.test.tsx | 52 ++++++++++++++++++- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/components/home/StartPointsCard.tsx b/src/components/home/StartPointsCard.tsx index 184768a..5290907 100644 --- a/src/components/home/StartPointsCard.tsx +++ b/src/components/home/StartPointsCard.tsx @@ -35,7 +35,6 @@ const StartCard: React.FC = ({ const journeysId = await fetchJourneybyPoint(Id); if (!journeysId || journeysId.length === 0) { - console.error('No journeys found for this point.'); return; } diff --git a/src/components/home/service/home.services.tsx b/src/components/home/service/home.services.tsx index 068ac56..95aa399 100644 --- a/src/components/home/service/home.services.tsx +++ b/src/components/home/service/home.services.tsx @@ -41,7 +41,7 @@ const JourneyService = () => { const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL_STUDIO}/points/`); return response.data; } catch (error) { - console.error('Erro ao buscar pontos:', error); + console.error('Erro ao buscar pontos de partida:', error); return null; } } @@ -51,7 +51,7 @@ const JourneyService = () => { const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL_STUDIO}/points/${id}/journeys`); return response.data; } catch (error) { - console.error(`Erro ao buscar jornada com ID ${id}:`, error); + console.error(`Erro ao buscar jornadas do Ponto de Partida com ID ${id}:`, error); return null; } } diff --git a/test/app/homePage/home.test.tsx b/test/app/homePage/home.test.tsx index bf3df7e..afd4408 100644 --- a/test/app/homePage/home.test.tsx +++ b/test/app/homePage/home.test.tsx @@ -10,7 +10,7 @@ jest.mock('sonner', () => ({ })); describe('JourneyService', () => { - const { fetchUserJourneys, fetchJourneys, fetchJourneyById } = JourneyService(); + const { fetchUserJourneys, fetchJourneys, fetchJourneyById, fetchPoints, fetchJourneybyPoint } = JourneyService(); describe('fetchUserJourneys', () => { it('should return an empty array and show an error toast if no session exists', async () => { @@ -83,4 +83,52 @@ describe('JourneyService', () => { consoleSpy.mockRestore(); }); }); -}); + + describe('fetchPoints', () => { + it('should return data when the request is successful', async () => { + const mockData = [{ id: '1', name: 'Start Point 1' }]; + (axios.get as jest.Mock).mockResolvedValue({ data: mockData }); + + const result = await fetchPoints(); + + expect(result).toEqual(mockData); + }); + + it('should return null and log an error if the request fails', async () => { + (axios.get as jest.Mock).mockRejectedValue(new Error('Network Error')); + + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + const result = await fetchPoints(); + + expect(result).toBeNull(); + expect(consoleSpy).toHaveBeenCalledWith('Erro ao buscar pontos de partida:', expect.any(Error)); + + consoleSpy.mockRestore(); + }); + }); + + describe('fetchJourneybyPoint', () => { + it('should return data when the request is successful', async () => { + const mockData = ['2', '3']; + (axios.get as jest.Mock).mockResolvedValue({ data: mockData }); + + const result = await fetchJourneybyPoint('1'); + + expect(result).toEqual(mockData); + }); + + it('should return null and log an error if the request fails', async () => { + (axios.get as jest.Mock).mockRejectedValue(new Error('Network Error')); + + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + const result = await fetchJourneybyPoint('1'); + + expect(result).toBeNull(); + expect(consoleSpy).toHaveBeenCalledWith('Erro ao buscar jornadas do Ponto de Partida com ID 1:', expect.any(Error)); + + consoleSpy.mockRestore(); + }); + }); +}); \ No newline at end of file