Skip to content

Commit d0a6af0

Browse files
committed
Integração de api no perfil do usuário
1 parent 6226646 commit d0a6af0

File tree

8 files changed

+103
-83
lines changed

8 files changed

+103
-83
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Construir um sistema de ponta a ponta que rastreia fóruns, processa o texto das
2020

2121
### Rode a migration
2222

23-
- Para criar o arquivo em databse.bd, no terminal, na pasta backend, execute: npx knex migrate:latest --knexfile knexfile.ts
23+
- Para criar o arquivo em databse.bd, no terminal, na pasta backend/src, execute: npx knex migrate:latest --knexfile knexfile.ts
2424

2525

2626
## Criar seeds (popular tabelas)

frontend/src/API/Auth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export async function Login(data: LoginProps) {
1818
}
1919

2020
const {user, token} = await response.json();
21+
console.log("Dados do usuário logado:", user);
2122
return { user, token };
2223

2324
}

frontend/src/API/Keywords.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export async function GetKeywords(): Promise<string[]> {
88
method: 'GET',
99
headers: {
1010
'Content-Type': 'application/json',
11-
// 'Authorization': `Bearer ${localStorage.getItem('token')}`
1211
},
1312
});
1413

@@ -17,11 +16,5 @@ export async function GetKeywords(): Promise<string[]> {
1716
}
1817

1918
const data = await response.json();
20-
21-
// IMPORTANTE: O componente TagInput do seu código original usa um array de strings.
22-
// Se sua API retorna objetos (ex: [{id: 1, name: "React"}]), faça um map:
23-
//return data.map((tech: any) => tech.name);
24-
25-
// Se a API já retorna direto ["React", "Java"], basta retornar:
2619
return data;
2720
}

frontend/src/API/Project.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
export interface ProjectProps {
2-
name: string;
2+
title: string;
33
description: string;
44
technologies: string[];
55
status: string;
6-
date: string;
6+
startDate: Date;
7+
}
8+
9+
export function parseDate(dataString: string): Date {
10+
// Divide a string "20/11/2025" em partes
11+
const [dia, mes, ano] = dataString.split('/');
12+
13+
// Cria a data: new Date(ano, mês - 1, dia)
14+
return new Date(Number(ano), Number(mes) - 1, Number(dia));
715
}
816

917
export async function NewProject(data: ProjectProps) {
@@ -40,7 +48,7 @@ export async function UpdateProject(projectId: string, data: ProjectProps) {
4048
} */
4149
}
4250

43-
export async function getFeedProjects(): Promise<ProjectProps[]> {
51+
export async function GetFeedProjects(): Promise<ProjectProps[]> {
4452
// Em um app real, você faria um fetch
4553
// const response = await fetch('http://localhost:3000/api/projects', ...);
4654
// const data = await response.json();
@@ -49,14 +57,14 @@ export async function getFeedProjects(): Promise<ProjectProps[]> {
4957
// Por agora, vamos simular a resposta da API:
5058
return [
5159
{
52-
name: 'Projeto CTable (React)',
60+
title: 'Projeto CTable (React)',
5361
description: 'Post da "ceci" (usuário logado)',
54-
technologies: ['React', 'TS'], status: 'em-andamento', date: '10/11/2025'
62+
technologies: ['React', 'TS'], status: 'em-andamento', startDate: parseDate('01/10/2026')
5563
},
5664
{
57-
name: 'Projeto de Outra Pessoa',
65+
title: 'Projeto de Outra Pessoa',
5866
description: 'Post de outro usuário...',
59-
technologies: ['Python'], status: 'finalizado', date: '01/10/2025'
67+
technologies: ['Python'], status: 'finalizado', startDate: parseDate('01/10/2025')
6068
}
6169
];
6270
}
@@ -74,4 +82,26 @@ export async function DeleteProject(projectId: string) {
7482
const errorData = await response.json();
7583
throw new Error(errorData.message || "Erro ao excluir projeto.");
7684
} */
77-
}
85+
}
86+
87+
export async function GetUserProjects(): Promise<ProjectProps[]> {
88+
const token = localStorage.getItem('token');
89+
90+
const response = await fetch('http://localhost:3000/api/user/projects', {
91+
method: 'GET',
92+
headers: {
93+
'Content-Type': 'application/json',
94+
'Authorization': `Bearer ${token}`, // Adiciona o Token JWT
95+
},
96+
});
97+
98+
if (!response.ok) {
99+
const errorData = await response.json();
100+
throw new Error(errorData.message);
101+
}
102+
103+
const data = await response.json();
104+
console.log("Dados dos projetos do usuário:", data.projects);
105+
106+
return data.projects;
107+
}

frontend/src/components/layout/Sidebar/index.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ import * as S from './styles';
77
interface Community {
88
id: string;
99
name: string;
10-
iconUrl: string;
1110
}
1211

1312
// --- DADOS MOCKADOS ---
1413
const mockCommunities: Community[] = [
15-
{ id: 'react', name: 'r/React', iconUrl: 'https://styles.redditmedia.com/t5_2zldd/styles/communityIcon_fbblpo38o0x11.png' },
16-
{ id: 'node', name: 'r/Node', iconUrl: 'https://styles.redditmedia.com/t5_2qgct/styles/communityIcon_i4e0vjze1w2y1.png' },
17-
{ id: 'python', name: 'r/Python', iconUrl: 'https://styles.redditmedia.com/t5_2qh0y/styles/communityIcon_h9b7f16n1af31.png' },
18-
{ id: 'scikit', name: 'r/Scikit-Learn', iconUrl: 'https://styles.redditmedia.com/t5_2tby0/styles/communityIcon_d8j4iz4o9k731.png' },
14+
{ id: 'react', name: 'r/React' },
15+
{ id: 'node', name: 'r/Node' },
16+
{ id: 'python', name: 'r/Python'},
17+
{ id: 'scikit', name: 'r/Scikit-Learn'},
1918
];
2019
// ----------------------
2120

@@ -52,7 +51,6 @@ export default function Sidebar() {
5251
key={community.id}
5352
>
5453
<S.CommunityIcon
55-
src={community.iconUrl}
5654
alt={`${community.name} icon`}
5755
/>
5856
<span>{community.name}</span>

frontend/src/pages/CreateProject/index.tsx

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import Toast from '../../components/common/Toast';
1111
import { useNavigate, useLocation, useParams } from 'react-router-dom';
1212
import type { NotificationState } from '../../components/common/Toast';
1313
import { GetKeywords } from '../../API/Keywords';
14+
import { parseDate } from '../../API/Project';
15+
16+
interface ProjectFormProps extends Omit<ProjectProps, 'startDate'> {
17+
startDate: string;
18+
}
1419

1520
export default function CreateProject() {
1621
const navigate = useNavigate();
@@ -23,14 +28,22 @@ export default function CreateProject() {
2328
const projectToEdit = location.state?.projectToEdit as (ProjectProps & { id: string }) | undefined;
2429
const isEditMode = !!projectToEdit; // Se projectToEdit existe, estamos editando
2530

26-
const { register, handleSubmit, control, watch } = useForm<ProjectProps>({
31+
const formatDateToString = (date?: Date | string) => {
32+
if (!date) return "";
33+
const d = new Date(date);
34+
// Verifica se é data válida
35+
if (isNaN(d.getTime())) return "";
36+
return d.toLocaleDateString('pt-BR'); // Retorna "20/11/2025"
37+
};
38+
39+
const { register, handleSubmit, control, watch } = useForm<ProjectFormProps>({
2740
// Preenche os valores padrão se estiver em modo de edição
2841
defaultValues: {
29-
name: projectToEdit?.name || "",
42+
title: projectToEdit?.title || "",
3043
description: projectToEdit?.description || "",
3144
technologies: projectToEdit?.technologies || [],
3245
status: projectToEdit?.status || "",
33-
date: projectToEdit?.date || ""
46+
startDate: formatDateToString(projectToEdit?.startDate)
3447
}
3548
});
3649

@@ -53,17 +66,23 @@ export default function CreateProject() {
5366
}, []);
5467

5568
// Lida com CRIAR ou ATUALIZAR
56-
const onSubmit = (data: ProjectProps) => {
69+
const onSubmit = (data: ProjectFormProps) => {
70+
71+
const finalData: ProjectProps = {
72+
...data,
73+
startDate: parseDate(data.startDate)
74+
};
75+
5776
try {
5877
if (isEditMode) {
5978
// MODO DE EDIÇÃO
60-
console.log("Atualizando Projeto:", projectId, data);
61-
// UpdateProject(projectId, data);
79+
console.log("Atualizando Projeto:", projectId, finalData);
80+
// UpdateProject(projectId, finalData);
6281
setNotification({ message: 'Projeto atualizado com sucesso!', type: 'success' });
6382
} else {
6483
// MODO DE CRIAÇÃO
65-
console.log("Criando Projeto:", data);
66-
NewProject(data);
84+
console.log("Criando Projeto:", finalData);
85+
NewProject(finalData);
6786
setNotification({ message: 'Projeto criado com sucesso!', type: 'success' });
6887
}
6988

@@ -97,8 +116,8 @@ export default function CreateProject() {
97116
<h2>{isEditMode ? 'Editar Projeto' : 'Criar Projeto'}</h2>
98117

99118
<S.InputGroup>
100-
<S.Label htmlFor="name">Nome do Projeto</S.Label>
101-
<S.Input id="name" {...register('name', { required: true })} />
119+
<S.Label htmlFor="title">Nome do Projeto</S.Label>
120+
<S.Input id="title" {...register('title', { required: true })} />
102121
</S.InputGroup>
103122

104123
<S.InputGroup>
@@ -120,21 +139,21 @@ export default function CreateProject() {
120139
</S.InputGroup>
121140

122141
<S.InputGroup>
123-
<S.Label htmlFor="date">Data de Início</S.Label>
142+
<S.Label htmlFor="startDate">Data de Início</S.Label>
124143
<Controller
125-
name="date"
144+
name="startDate"
126145
control={control}
127-
render={({ field }) => (
146+
render={({ field: { onChange, value } }) => (
128147
<S.Input
129148
as={IMaskInput}
130149
mask="00/00/0000"
131-
id="date"
150+
id="startDate"
132151
placeholder="DD/MM/AAAA"
133-
{...field}
134-
// 9. Desabilita a data se estiver em modo de edição
152+
value={value}
153+
onAccept={(value: string) => onChange(value)}
135154
disabled={isEditMode}
136155
/>
137-
)}
156+
)}
138157
/>
139158
</S.InputGroup>
140159

frontend/src/pages/Login/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,23 @@ import Toast from '../../components/common/Toast';
1313
import { useNavigate } from 'react-router-dom';
1414
import {Login} from '../../API/Auth'
1515
import type { LoginProps } from '../../API/Auth';
16+
import { useAuth } from '../../API/AuthContext';
1617
import type { NotificationState } from '../../components/common/Toast';
1718

1819
export default function LoginPage() {
1920
const [notification, setNotification] = useState<NotificationState | null>(null);
20-
21+
const { login } = useAuth();
2122

2223
const { register, handleSubmit, formState: {isSubmitting} } = useForm<LoginProps>();
2324
const navigate = useNavigate();
2425

2526
async function onSubmit(data: LoginProps) {
2627
console.log(data);
2728
try{
28-
await Login(data);
29+
//await Login(data);
30+
await login(data);
2931

30-
console.log('Usuário registrado com sucesso:');
32+
console.log('Usuário registrado com sucesso');
3133

3234
// Define estado para mostrar notificação de sucesso
3335
setNotification({ message: 'Usuário registrado com sucesso!', type: 'success' });

frontend/src/pages/Profile/index.tsx

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@ import * as S from './styles';
55
import * as D from '../../components/common/Dropdown/styles';
66
import type { ProjectProps } from '../../API/Project';
77
import type { CommentProps } from '../../API/Comment';
8-
9-
// 3. Tipagem do Usuário
10-
type UserProps = {
11-
username: string;
12-
avatarUrl: string;
13-
};
8+
import { GetUserProjects } from '../../API/Project';
9+
import { useAuth } from '../../API/AuthContext';
1410

1511
// --- ÍCONES ---
1612
const PostsIcon = () => (
@@ -34,43 +30,23 @@ const SettingsIcon = () => (
3430
type ViewState = 'posts' | 'comments';
3531

3632
export default function Profile() {
33+
const { currentUser, logout } = useAuth();
3734
const [view, setView] = useState<ViewState>('posts');
3835
const [isMenuOpen, setIsMenuOpen] = useState(false);
3936
const menuRef = useRef<HTMLDivElement>(null);
4037

4138
// Estados para os dados da API
42-
const [user, setUser] = useState<UserProps | null>(null);
4339
const [userPosts, setUserPosts] = useState<ProjectProps[]>([]);
4440
const [userComments, setUserComments] = useState<CommentProps[]>([]);
4541

46-
// Simulação de chamada de API (useEffect)
4742
useEffect(() => {
4843
// Função assíncrona para buscar todos os dados
44+
console.log("Efeito de busca de dados do perfil disparado.");
4945
const fetchProfileData = async () => {
5046
try {;
51-
52-
// Dados que viriam da sua API
53-
const apiUserData: UserProps = {
54-
username: 'ceci',
55-
avatarUrl: '' // URL da imagem do avatar
56-
};
5747

58-
const apiUserPosts: ProjectProps[] = [
59-
{
60-
name: 'Projeto CTable (React)',
61-
description: 'Este é o projeto que estamos construindo agora!',
62-
technologies: ['React', 'TypeScript', 'Node.js'],
63-
status: 'em-andamento',
64-
date: '10/11/2025'
65-
},
66-
{
67-
name: 'API de Análise de Dados',
68-
description: 'Um backend em Python para análise.',
69-
technologies: ['Python', 'Django'],
70-
status: 'finalizado',
71-
date: '01/08/2025'
72-
}
73-
];
48+
console.log("Usuário atual no Profile:", currentUser);
49+
const apiUserPosts = await GetUserProjects();
7450

7551
const apiUserComments: CommentProps[] = [
7652
{
@@ -80,18 +56,17 @@ export default function Profile() {
8056
}
8157
];
8258

83-
// Atualiza os estados
84-
setUser(apiUserData);
8559
setUserPosts(apiUserPosts);
8660
setUserComments(apiUserComments);
8761

8862
} catch (error) {
8963
console.error("Falha ao buscar dados do perfil:", error);
9064
}
65+
9166
};
92-
93-
fetchProfileData();
94-
}, []); // O array vazio [] garante que isso rode apenas uma vez
67+
if(currentUser)
68+
fetchProfileData();
69+
}, [currentUser]);
9570

9671
// Lógica para fechar o menu ao clicar fora
9772
useEffect(() => {
@@ -110,11 +85,13 @@ export default function Profile() {
11085

11186
// Função para renderizar o feed (posts ou comentários)
11287
const renderFeed = () => {
113-
11488
if (view === 'posts') {
115-
// Mapeia 'userPosts' e passa o 'post' (ProjectProps)
116-
return userPosts.map(post => (
117-
<S.PostContainer >
89+
if (userPosts.length === 0) {
90+
return <p style={{ color: '#fff', padding: '20px' }}>Nenhum projeto encontrado.</p>;
91+
}
92+
93+
return userPosts.map((post, index) => (
94+
<S.PostContainer key={index}>
11895
<Postcard post={post} showMenu={true} />
11996
</S.PostContainer>
12097
));
@@ -179,10 +156,10 @@ export default function Profile() {
179156
<S.ProfileInfo>
180157
<S.ProfileAvatar
181158
style={{
182-
backgroundImage: user?.avatarUrl ? `url(${user.avatarUrl})` : undefined
159+
backgroundImage: undefined
183160
}}
184161
/>
185-
<S.Username>{user ? user.username : '...'}</S.Username>
162+
<S.Username>{currentUser?.nomeCompleto || currentUser?.username || 'Carregando...'}</S.Username>
186163
</S.ProfileInfo>
187164

188165
<S.PostList>

0 commit comments

Comments
 (0)