Skip to content

Commit

Permalink
Merge pull request #25 from fga-eps-mds/qas
Browse files Browse the repository at this point in the history
[FEAT] Adiciona lógica de disciplinas (fga-eps-mds/2024.2-ARANDU-DOC#71)
  • Loading branch information
gabrielm2q authored Feb 2, 2025
2 parents 5d3387b + 67546a6 commit 5129064
Show file tree
Hide file tree
Showing 31 changed files with 2,189 additions and 57 deletions.
5 changes: 4 additions & 1 deletion .env.dev.template
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# NEXT_PUBLIC_API_URL_USER=http://user-api:3000
# NEXT_PUBLIC_API_URL_STUDIO=http://studio-api:3002
# NEXTAUTH_URL=http://front-api:4000
NEXT_PUBLIC_API_URL_USER=
NEXT_PUBLIC_API_URL_STUDIO=
NEXTAUTH_URL=
NEXTAUTH_SECRET=
JWT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
PORT=
NODE_ENV=
4 changes: 2 additions & 2 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Box, Grid, Link } from '@mui/material';
import Image from 'next/image';
import calcuclusLogo from '@/public/calculus-logo.svg';
import { GoogleAuthButton } from '@/components/ui/buttons/googleAuth.button';
import { MicrosoftAuthButton } from '@/components/ui/buttons/microsoftAuth.button';
import { SingInForm } from '@/components/forms/signInForm';
import LogoBlack from '@/public/Logo-Black-White.svg'

export default function LoginPage() {
return (
Expand All @@ -12,7 +12,7 @@ export default function LoginPage() {
<Box className="flex flex-col justify-around mb-2">
<Image
className="self-center"
src={calcuclusLogo}
src={LogoBlack}
alt="Logo"
width={96}
height={96}
Expand Down
3 changes: 2 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useRouter } from 'next/navigation';
import roboProfessor from '@/public/robo_professor.png';
import MyButton from '@/components/ui/buttons/myButton.component';
import { useSession } from 'next-auth/react';
import { amarante } from '@/components/ui/fonts/fonts';

export default function LandingPage() {
const { data: session } = useSession();
Expand All @@ -21,7 +22,7 @@ export default function LandingPage() {
return (
<div className="min-h-screen flex flex-col bg-[#fffafa]">
<header className="w-full flex justify-between p-4">
<h1 className="text-4xl font-bold text-[#1F1F1F]">Calculus</h1>
<h1 className={`text-4xl ${amarante.className} font-bold text-[#1F1F1F]`}>ARANDU</h1>
<Link href="/login">
<MyButton width="120px" height="50px" color="white" bold>
Login
Expand Down
159 changes: 159 additions & 0 deletions src/app/subjects/[...pointId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
'use client';

import React, { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import {
Box,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Button,
Typography,
CircularProgress,
} from '@mui/material';
import ButtonRed from '@/components/ui/buttons/red.button';
import SearchBar from '@/components/admin/SearchBar';
import SubjectTable from '@/components/tables/subject.table';
import { Subject } from '@/lib/interfaces/subjetc.interface';
import {
deleteSubjects,
GetSubjectsByUserId,
GetSubjects
} from '@/services/studioMaker.service';
import Popup from '@/components/ui/popup';
import { SubjectForm } from '@/components/forms/subject.form';
import { toast } from 'sonner';
import { updateSubject, addSubject, handleSubjectAction, handleRemoveSubject, handleMenuOpen, fetchSubjects, handleMenuClose } from './subject.functions';

export default function SubjectPage({
params,
}: {
readonly params: { pointId: string };
}) {

const [listSubjects, setListSubjects] = useState<Subject[]>([]);
const [filteredSubjects, setFilteredSubjects] = useState<Subject[]>([]);

const {
data = [],
isLoading,
error,
} = useQuery<Subject[], Error>({
queryKey: ['subjects', params.pointId],
queryFn: () => fetchSubjects(params, setListSubjects, setFilteredSubjects), // Corrigido: Chamando a função corretamente com uma função anônima
});

const [searchQuery, setSearchQuery] = useState<string>('');

const [selectedSubject, setSelectedSubject] = useState<Subject | null>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [exclusionDialogOpen, setExclusionDialogOpen] =
useState<boolean>(false);
const [editionDialogOpen, setEditionDialogOpen] = useState<boolean>(false);
const [createDialogOpen, setCreateDialogOpen] = useState(false);

useEffect(() => {
if (searchQuery === '') {
setFilteredSubjects(listSubjects);
} else {
const lowercasedQuery = searchQuery.toLowerCase();
const filtered = listSubjects.filter((subject) => {
return (
subject.name.toLowerCase().includes(lowercasedQuery) ||
subject.description.toLowerCase().includes(lowercasedQuery)
);
});

setFilteredSubjects(filtered);
}
}, [searchQuery, listSubjects]);

if (isLoading) {
return <CircularProgress />;
}

if (error) {
return <Typography>Error fetching subjects</Typography>;
}


const SubjectPage = (
<Box
sx={{
padding: 2,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Typography variant="h2">Disciplinas</Typography>
<Box sx={{ width: '100%', maxWidth: 800, marginBottom: 2 }}>
<SearchBar value={searchQuery} onChange={setSearchQuery} />
</Box>
<Box sx={{ width: '100%', maxWidth: 800 }}><SubjectTable
subjects={filteredSubjects}
anchorEl={anchorEl}
onMenuClick={(event, subject) => handleMenuOpen(event, subject, setAnchorEl, setSelectedSubject)}
onMenuClose={() => handleMenuClose(setAnchorEl)}
onSubjectAction={(action) => handleSubjectAction(action, setEditionDialogOpen, setExclusionDialogOpen)}
/></Box>



<ButtonRed onClick={() => setCreateDialogOpen(true)}>
Nova Disciplina
</ButtonRed>

<Popup
openPopup={editionDialogOpen}
setPopup={setEditionDialogOpen}
title="Editar Disciplina"
>
<SubjectForm
callback={(subject: Subject) => updateSubject(subject, listSubjects, setListSubjects)}
subject={selectedSubject!}
setDialog={setEditionDialogOpen}
/>

</Popup>

<Popup
openPopup={createDialogOpen}
setPopup={setCreateDialogOpen}
title="Criar Nova Disciplina"
>
<SubjectForm
callback={(subject: Subject) => addSubject(subject, listSubjects, setListSubjects)}
setDialog={setCreateDialogOpen}
/>
</Popup>

<Dialog
open={exclusionDialogOpen}
onClose={() => setExclusionDialogOpen(false)}
>
<DialogTitle>Confirmar Exclusão de Disciplina</DialogTitle>
<DialogContent>
{selectedSubject && (
<Typography variant="h6">{`Excluir ${selectedSubject.name}?`}</Typography>
)}
</DialogContent>
<DialogActions>
<Button onClick={() => setExclusionDialogOpen(false)} color="error">
Cancelar
</Button>
<Button
onClick={() => handleRemoveSubject(selectedSubject!, listSubjects, setListSubjects, setExclusionDialogOpen)}
color="primary"
>
Confirmar
</Button>
</DialogActions>
</Dialog>
</Box >
);

return SubjectPage

}
96 changes: 96 additions & 0 deletions src/app/subjects/[...pointId]/subject.functions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// subjectFunctions.ts
import { Subject } from '@/lib/interfaces/subjetc.interface';
import { deleteSubjects, GetSubjects, GetSubjectsByUserId } from '@/services/studioMaker.service';
import { toast } from 'sonner';

export const updateSubject = (subject: Subject, listSubjects: Subject[], setListSubjects: React.Dispatch<React.SetStateAction<Subject[]>>) => {
// Verificar se listSubjects não é undefined ou null
if (listSubjects && Array.isArray(listSubjects)) {
setListSubjects(
listSubjects.map((s) => (s._id === subject._id ? subject : s)),
);
}
};

export const addSubject = (
subject: Subject,
listSubjects: Subject[],
setListSubjects: React.Dispatch<React.SetStateAction<Subject[]>>
) => {
// Verificar se listSubjects não é undefined ou null
if (listSubjects && Array.isArray(listSubjects)) {
setListSubjects(
[...listSubjects, subject].sort((a, b) => a.order - b.order),
);
}
};

export const handleSubjectAction = (
action: string,
setEditionDialogOpen: React.Dispatch<React.SetStateAction<boolean>>,
setExclusionDialogOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
if (action === 'editar') {
setEditionDialogOpen(true);
}
if (action === 'excluir') {
setExclusionDialogOpen(true);
}
};

export const handleRemoveSubject = async (
subject: Subject,
listSubjects: Subject[],
setListSubjects: React.Dispatch<React.SetStateAction<Subject[]>>,
setExclusionDialogOpen: React.Dispatch<React.SetStateAction<boolean>>
) => {
const response = await deleteSubjects({
id: subject._id,
token: JSON.parse(localStorage.getItem('token')!),
});
if (response.data) {
toast.success('Disciplina excluída com sucesso!');
setListSubjects(listSubjects.filter((s) => s._id !== subject._id));
setExclusionDialogOpen(false);
} else {
toast.error('Erro ao excluir disciplina. Tente novamente mais tarde!');
}
};

export const handleMenuOpen = (
event: React.MouseEvent<HTMLButtonElement>,
subject: Subject,
setAnchorEl: React.Dispatch<React.SetStateAction<null | HTMLElement>>,
setSelectedSubject: React.Dispatch<React.SetStateAction<Subject | null>>
) => {
setAnchorEl(event.currentTarget);
setSelectedSubject(subject);
};

export const fetchSubjects = async (
params: { pointId: string },
setListSubjects: React.Dispatch<React.SetStateAction<Subject[]>>,
setFilteredSubjects: React.Dispatch<React.SetStateAction<Subject[]>>
): Promise<Subject[]> => {
let subjects: Subject[];

// Verifica qual função chamar baseado no pointId
if (params.pointId == "admin") {
subjects = await GetSubjects(); // Busca todas as disciplinas
} else {
subjects = await GetSubjectsByUserId(params.pointId); // Busca as disciplinas pelo userId
}

// Ordena os subjects pelo campo 'order'
subjects.sort((a, b) => a.order - b.order);

// Atualiza os estados com as disciplinas ordenadas
setListSubjects(subjects);
setFilteredSubjects(subjects);

return subjects;
};

export const handleMenuClose = (setAnchorEl: React.Dispatch<React.SetStateAction<null | HTMLElement>>,) => {
setAnchorEl(null);
};
1 change: 1 addition & 0 deletions src/components/admin/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const SearchBar: React.FC<SearchBarProps> = ({ value, onChange }) => {
variant="outlined"
fullWidth
value={value}
role="searchbox"
onChange={(e) => onChange(e.target.value)}
InputProps={{
startAdornment: (
Expand Down
26 changes: 16 additions & 10 deletions src/components/forms/journey.form.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client';

import { Box, TextField } from '@mui/material';
import { useForm, SubmitHandler } from 'react-hook-form';
import MyButton from '@/components/ui/buttons/myButton.component';
import { zodResolver } from '@hookform/resolvers/zod';
import { Box, TextField } from '@mui/material';
import { SubmitHandler, useForm } from 'react-hook-form';
import { toast } from 'sonner';
import MyButton from '@/components/ui/buttons/myButton.component';

import { journeySchema, JourneySchemaData } from '@/lib/schemas/journey.schema';
import {
Expand All @@ -13,6 +13,11 @@ import {
} from '@/services/studioMaker.service';

export function JourneyForm({ callback, journey, setDialog, pointId }: any) {

const urlAtual = window.location.href;
const match = urlAtual.match(/\/journey\/([a-zA-Z0-9]+)$/);
const extractedId = match ? match[1] : null;

const {
register,
handleSubmit,
Expand All @@ -21,20 +26,21 @@ export function JourneyForm({ callback, journey, setDialog, pointId }: any) {
resolver: zodResolver(journeySchema),
defaultValues: {
title: journey ? journey.title : '',
subjectId: extractedId
},
});

const onSubmit: SubmitHandler<JourneySchemaData> = async (data) => {
const response = journey
? await updateJourneyById({
id: journey._id,
data,
token: JSON.parse(localStorage.getItem('token')!),
})
id: journey._id,
data,
token: JSON.parse(localStorage.getItem('token')!),
})
: await createJourney({
data: { pointId, ...data },
token: JSON.parse(localStorage.getItem('token')!),
});
data: { pointId, ...data },
token: JSON.parse(localStorage.getItem('token')!),
});
if (response.data) {
toast.success('Jornada com sucesso!');
callback(response.data);
Expand Down
1 change: 1 addition & 0 deletions src/components/forms/signInForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function SingInForm() {
'refresh',
JSON.stringify(session?.user.refreshToken),
);
localStorage.setItem('id', JSON.stringify(session.user.id))
toast.success('Login realizado com sucesso!');
router.push('/home');
}
Expand Down
Loading

0 comments on commit 5129064

Please sign in to comment.