@@ -119,11 +382,13 @@
leave-from-class="transform opacity-100 translate-y-0"
leave-to-class="transform opacity-0 translate-y-2">
+
- Biography
+ Biography
+
{{ userProfile.biography }}
@@ -134,13 +399,12 @@
Recent Publications
- {{ userProfile.publications.length }}
+ {{ userProfile.publications.length }} Papers
-
-
-
+
-
-
-
-
+ class="relative overflow-hidden rounded-2xl border-2 border-dashed border-slate-300 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/50 p-12 text-center hover:border-blue-400 dark:hover:border-blue-500 transition-all duration-300 group">
+
+
No
+ publications yet
+
-
-
- No publications yet
-
-
- Your profile is looking a little empty. Share your research with the
- community to start building your portfolio.
-
-
-
-
+
-
-
{{ post.rating }}
-
-
{{ post.type.replace('_', ' ') }}
- {{ post.publication_date
- }}
+ {{ post.publishedDate }}
{{
- post.title }}
-
-
By
- {{
- post.authors.join(', ') }}
-
+ post.title }}
+
By {{
+ post.authors.join(', ') }}
💬 {{ post.comment_count }} Comments
#{{ tag.name
- }}
+ class="text-blue-600 dark:text-blue-400">#{{ tag.name }}
+
@@ -322,7 +556,6 @@
-
@@ -351,6 +584,8 @@ const filterType = ref('ALL'); // 'ALL' | 'CONFERENCE_PAPER' | 'RESEARCH_POST'
const page = ref(1);
// --- Interfaces ---
+const selectedPost = ref(null);
+const newCommentContent = ref('');
interface Tag {
id: number;
@@ -401,6 +636,28 @@ const userProfile = ref({
});
// --- API Actions ---
+const getFileName = (fullPath: string) => {
+ if (!fullPath) return null;
+ return fullPath.split(/[/\\]/).pop();
+};
+
+const downloadFile = async (post: any) => {
+ if (!post.attachedFile) return;
+ const filename = getFileName(post.attachedFile);
+ const downloadUrl = `${api}/publications/download/${filename}`; // Verifica se este endpoint existe no teu backend
+
+ try {
+ // Método simples para download (se o endpoint for público ou lidar com cookies)
+ const link = document.createElement('a');
+ link.href = downloadUrl;
+ link.setAttribute('download', filename || 'download');
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ } catch (e) {
+ console.error("Download error:", e);
+ }
+};
// Fetch Tags (EP23) - To populate the select box
async function fetchTags() {
@@ -416,13 +673,17 @@ async function fetchTags() {
}
}
-const fetchPostDetail = async (id) => {
+const fetchPostDetail = async (id: number) => {
loading.value = true;
try {
const postDetail = await $fetch(`${api}/publications/${id}`, {
headers: { Authorization: `Bearer ${token.value}` }
});
+
selectedPost.value = postDetail;
+
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+
} catch (err) {
console.error("Failed to fetch post detail:", err);
} finally {
@@ -466,13 +727,10 @@ async function fetchProfileData() {
loading.value = true;
try {
- // Fetch User Details
const userData = await $fetch(`${api}/users/me`, {
method: 'GET',
headers: { Authorization: `Bearer ${token.value}` }
});
-
- // Fetch User Publications
const publicationsData = await $fetch(`${api}/publications/me`, {
method: 'GET',
headers: { Authorization: `Bearer ${token.value}` },
@@ -484,7 +742,6 @@ async function fetchProfileData() {
}
});
- // Map Data
userProfile.value = {
...userProfile.value,
id: userData.id,
@@ -497,14 +754,18 @@ async function fetchProfileData() {
userProfile.value.publications = (publicationsData as any[]).map(pub => {
const pubDate = new Date(pub.publication_date);
+
return {
- id: pub.id,
- title: pub.title,
- abstract: pub.abstract || 'No abstract available.',
+ ...pub, // Manter todos os campos originais da API
+
publishedDate: pubDate.toLocaleDateString(),
publishedYear: pubDate.getFullYear().toString(),
citations: pub.rating || 0,
- field: pub.tags && pub.tags.length > 0 ? pub.tags[0].name : 'General'
+
+ authors: pub.authors || [],
+ tags: pub.tags || [],
+ comment_count: pub.comment_count || 0,
+ rating: pub.rating || 0
};
});
@@ -515,6 +776,39 @@ async function fetchProfileData() {
}
}
+const votePost = async (targetPost, direction) => {
+ if (!targetPost) return;
+
+ let voteTypeToSend = 'NONE';
+ const currentVote = targetPost.my_vote || 'NONE';
+
+ if (direction === 1) {
+ voteTypeToSend = currentVote === 'UP' ? 'NONE' : 'UP';
+ } else if (direction === -1) {
+ voteTypeToSend = currentVote === 'DOWN' ? 'NONE' : 'DOWN';
+ }
+
+ try {
+ const response = await $fetch(`${api}/publications/${targetPost.id}/vote`, {
+ method: 'PUT',
+ headers: { Authorization: `Bearer ${token.value}` },
+ body: { vote_type: voteTypeToSend }
+ });
+
+ targetPost.rating = response.score;
+ targetPost.my_vote = response.my_vote;
+
+ if (selectedPost.value && selectedPost.value.id === targetPost.id) {
+ selectedPost.value.rating = response.score;
+ selectedPost.value.my_vote = response.my_vote;
+ }
+
+ } catch (error) {
+ console.error("Vote failed", error);
+ alert("Failed to submit vote.");
+ }
+};
+
// --- Redirects ---
const goToCreatePost = () => {
console.log("Navigating to create post modal...");
@@ -530,3 +824,29 @@ onMounted(() => {
fetchActivityHistory(); // Fetch history on mount
});
+
+