LET'S FUCKING GOOOOO #18
+607
-356
File diff suppressed because it is too large
Load Diff
+112
-24
@@ -140,6 +140,41 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
|
||||
<div
|
||||
class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
|
||||
<div class="flex flex-wrap items-center gap-2 w-full sm:w-auto">
|
||||
<div class="relative flex-1 sm:flex-none">
|
||||
<input v-model="searchQuery" type="text" placeholder="Search posts..."
|
||||
class="w-full sm:w-48 bg-white dark:bg-gray-900 border dark:border-gray-800 rounded-lg pl-3 pr-3 py-2 text-xs font-bold outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<select v-model="sortBy"
|
||||
class="bg-white dark:bg-gray-900 border dark:border-gray-800 rounded-l-lg px-3 py-2 text-xs font-bold uppercase outline-none focus:ring-1 focus:ring-blue-500 cursor-pointer border-r-0">
|
||||
<option value="date">Date</option>
|
||||
<option value="votes">Rating</option>
|
||||
<option value="comments">Comments</option>
|
||||
</select>
|
||||
<button
|
||||
class="bg-white dark:bg-gray-900 border dark:border-gray-800 rounded-r-lg px-3 py-2 text-xs font-bold hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
||||
:title="sortDesc ? 'Descending' : 'Ascending'"
|
||||
@click="sortDesc = !sortDesc">
|
||||
<span class="inline-block transition-transform duration-200"
|
||||
:class="{ 'rotate-180': !sortDesc }">
|
||||
▼
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<select v-model="filterType"
|
||||
class="bg-white dark:bg-gray-900 border dark:border-gray-800 rounded-lg px-3 py-2 text-xs font-bold uppercase outline-none focus:ring-1 focus:ring-blue-500 cursor-pointer">
|
||||
<option value="ALL">All Types</option>
|
||||
<option value="CONFERENCE_PAPER">Conference Paper</option>
|
||||
<option value="RESEARCH_POST">Research Post</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="userProfile.publications.length === 0"
|
||||
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">
|
||||
|
||||
@@ -177,25 +212,50 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="pub in userProfile.publications" :key="pub.id"
|
||||
class="bg-white dark:bg-slate-900 p-6 rounded-xl shadow-sm border border-slate-200 dark:border-slate-800 hover:shadow-md hover:border-blue-300 dark:hover:border-blue-700 transition-all duration-200 group relative">
|
||||
<div class="flex justify-between items-start gap-4 mb-2">
|
||||
<h3 class="text-base font-bold text-slate-900 dark:text-slate-100">{{ pub.title
|
||||
}}</h3>
|
||||
<span
|
||||
class="shrink-0 px-2.5 py-1 rounded-md bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400 text-xs font-bold border border-slate-200 dark:border-slate-700">
|
||||
{{ pub.publishedYear }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-slate-600 dark:text-slate-400 text-sm leading-relaxed line-clamp-2">
|
||||
{{ pub.abstract }}</p>
|
||||
<div
|
||||
class="mt-4 pt-4 border-t border-slate-50 dark:border-slate-800 flex flex-wrap items-center gap-4 text-xs font-medium">
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 border border-blue-100 dark:border-blue-800/50">
|
||||
{{ pub.field }}
|
||||
</span>
|
||||
<span>{{ pub.citations }} Citations</span>
|
||||
<div v-for="post in userProfile.publicationsData" :key="post.id"
|
||||
class="group bg-white dark:bg-gray-900 border dark:border-gray-800 rounded-xl p-5 shadow-sm hover:border-blue-500/50 transition-all cursor-pointer mb-4"
|
||||
@click="fetchPostDetail(post.id)">
|
||||
<div class="flex gap-4">
|
||||
<div
|
||||
class="flex flex-col items-center gap-1 text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800/50 p-2 rounded-lg h-fit">
|
||||
|
||||
<button class="hover:text-blue-500 transition-colors text-lg"
|
||||
:class="{ 'text-orange-500': post.my_vote === 'UP' }"
|
||||
@click.stop="votePost(post, 1)">▲</button>
|
||||
|
||||
<span class="text-xs font-bold">{{ post.rating }}</span>
|
||||
|
||||
<button class="hover:text-red-500 transition-colors text-lg"
|
||||
:class="{ 'text-blue-500': post.my_vote === 'DOWN' }"
|
||||
@click.stop="votePost(post, -1)">▼</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span
|
||||
class="text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400">
|
||||
{{ post.type.replace('_', ' ') }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500">{{ post.publication_date
|
||||
}}</span>
|
||||
</div>
|
||||
<h2 class="text-lg font-bold text-gray-800 dark:text-white mb-2">{{
|
||||
post.title }}
|
||||
</h2>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">By
|
||||
{{
|
||||
post.authors.join(', ') }}
|
||||
</p>
|
||||
<div
|
||||
class="flex items-center gap-4 text-xs font-bold text-gray-500 uppercase">
|
||||
<span>💬 {{ post.comment_count }} Comments</span>
|
||||
<div class="flex gap-2">
|
||||
<span v-for="tag in post.tags" :key="tag.id"
|
||||
class="text-blue-600 dark:text-blue-400">#{{ tag.name
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -282,6 +342,14 @@ const authStore = useAuthStore();
|
||||
const { token } = storeToRefs(authStore);
|
||||
const router = useRouter();
|
||||
|
||||
// --- Reactive Variables ---
|
||||
const availableTags = ref<Tag[]>([]); // For tag select viewBox
|
||||
const searchQuery = ref('');
|
||||
const sortBy = ref('date'); // 'date' | 'votes' | 'comments'
|
||||
const sortDesc = ref(true);
|
||||
const filterType = ref('ALL'); // 'ALL' | 'CONFERENCE_PAPER' | 'RESEARCH_POST'
|
||||
const page = ref(1);
|
||||
|
||||
// --- Interfaces ---
|
||||
|
||||
interface Tag {
|
||||
@@ -348,6 +416,20 @@ async function fetchTags() {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchPostDetail = async (id) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const postDetail = await $fetch(`${api}/publications/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token.value}` }
|
||||
});
|
||||
selectedPost.value = postDetail;
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch post detail:", err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Computed ---
|
||||
const totalCitations = computed(() =>
|
||||
userProfile.value.publications.reduce((sum, pub) => sum + (pub.citations || 0), 0)
|
||||
@@ -390,11 +472,17 @@ async function fetchProfileData() {
|
||||
headers: { Authorization: `Bearer ${token.value}` }
|
||||
});
|
||||
|
||||
// Fetch Publications
|
||||
// const publicationsData = await $fetch(`${api}/publications/my-submissions`, {
|
||||
// method: 'GET',
|
||||
// headers: { Authorization: `Bearer ${token.value}` }
|
||||
// });
|
||||
// Fetch User Publications
|
||||
const publicationsData = await $fetch(`${api}/publications/me`, {
|
||||
method: 'GET',
|
||||
headers: { Authorization: `Bearer ${token.value}` },
|
||||
params: {
|
||||
page: page.value,
|
||||
sort: sortBy.value,
|
||||
order: sortDesc.value ? 'desc' : 'asc',
|
||||
type: filterType.value !== 'ALL' ? filterType.value : undefined
|
||||
}
|
||||
});
|
||||
|
||||
// Map Data
|
||||
userProfile.value = {
|
||||
|
||||
Reference in New Issue
Block a user