final fe part 1
This commit is contained in:
@@ -5,12 +5,14 @@ const props = defineProps<{
|
||||
isOpen: boolean;
|
||||
api: string;
|
||||
token: string | null;
|
||||
postToEdit?: any;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['close', 'refresh']);
|
||||
|
||||
interface Tag { id: number; name: string; }
|
||||
|
||||
const notifications = useNotificationStore();
|
||||
const isSubmitting = ref(false);
|
||||
const availableTags = ref<Tag[]>([]);
|
||||
const errors = reactive({
|
||||
@@ -73,26 +75,41 @@ async function submitPublication() {
|
||||
authors: uploadForm.authors,
|
||||
type: uploadForm.type,
|
||||
abstract: uploadForm.abstract,
|
||||
tags: uploadForm.selectedTagIds,
|
||||
// Sending fixed defaults for removed fields to maintain API compatibility if needed
|
||||
publication_date: new Date().toISOString().split('T')[0],
|
||||
is_confidential: false
|
||||
tags: uploadForm.selectedTagIds
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
if (uploadForm.file) formData.append('file', uploadForm.file);
|
||||
formData.append('data', JSON.stringify(metadata));
|
||||
const isEditing = !!props.postToEdit;
|
||||
const url = isEditing
|
||||
? `${props.api}/publications/${props.postToEdit.id}`
|
||||
: `${props.api}/publications`;
|
||||
|
||||
await $fetch(`${props.api}/publications`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${props.token}` },
|
||||
body: formData
|
||||
let requestBody: any;
|
||||
let headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${props.token}`
|
||||
};
|
||||
|
||||
if (!isEditing || uploadForm.file) {
|
||||
const formData = new FormData();
|
||||
if (uploadForm.file) formData.append('file', uploadForm.file);
|
||||
formData.append('data', JSON.stringify(metadata));
|
||||
requestBody = formData;
|
||||
}
|
||||
else {
|
||||
requestBody = metadata;
|
||||
}
|
||||
|
||||
await $fetch(url, {
|
||||
method: isEditing ? 'PATCH' : 'POST',
|
||||
headers,
|
||||
body: requestBody
|
||||
});
|
||||
|
||||
notifications.success(`Publication ${isEditing ? "updated" : "created"}`);
|
||||
emit('refresh');
|
||||
closeModal();
|
||||
} catch (error) {
|
||||
alert("Upload failed.");
|
||||
console.error("Submission error:", error);
|
||||
notifications.error("Operation failed");
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
@@ -113,19 +130,34 @@ function handleFileChange(event: Event) {
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
uploadForm.title = '';
|
||||
uploadForm.abstract = '';
|
||||
uploadForm.type = 'CONFERENCE_PAPER';
|
||||
uploadForm.newAuthorName = '';
|
||||
uploadForm.authors = [];
|
||||
uploadForm.selectedTagIds = [];
|
||||
uploadForm.file = null;
|
||||
emit('close');
|
||||
}
|
||||
|
||||
watch(() => props.isOpen, (newVal) => {
|
||||
if (newVal && availableTags.value.length === 0) fetchTags();
|
||||
if (!newVal) {
|
||||
// Reset errors on close
|
||||
errors.title = '';
|
||||
errors.abstract = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Helper for colorful tags
|
||||
watch(() => props.postToEdit, (newPost) => {
|
||||
if (newPost) {
|
||||
uploadForm.title = newPost.title;
|
||||
uploadForm.abstract = newPost.abstractText || newPost.abstract;
|
||||
uploadForm.type = newPost.type;
|
||||
uploadForm.authors = [...newPost?.authors];
|
||||
uploadForm.selectedTagIds = newPost?.tags.map((t: any) => t.id);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
const getTagClass = (id: number) => {
|
||||
const colors = [
|
||||
'bg-pink-100 text-pink-700 border-pink-200 dark:bg-pink-900/30 dark:text-pink-300',
|
||||
@@ -204,9 +236,6 @@ const getTagClass = (id: number) => {
|
||||
<select v-model="uploadForm.type"
|
||||
class="w-full rounded-lg border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-900 dark:text-white shadow-sm focus:ring-blue-500 sm:text-sm py-2.5 px-3 border">
|
||||
<option value="CONFERENCE_PAPER">Conference Paper</option>
|
||||
<option value="ARTICLE">Article</option>
|
||||
<option value="THESIS">Thesis</option>
|
||||
<option value="REPORT">Report</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -224,7 +253,7 @@ const getTagClass = (id: number) => {
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-8">
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="w-full">
|
||||
<label
|
||||
class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Authors</label>
|
||||
<div class="relative flex items-center">
|
||||
@@ -241,7 +270,7 @@ const getTagClass = (id: number) => {
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2 mt-3">
|
||||
<div :class="['flex flex-wrap gap-2', uploadForm.authors.length != 0 ? 'mt-3' : '']">
|
||||
<span v-for="(author, index) in uploadForm.authors" :key="index"
|
||||
class="inline-flex items-center gap-1 px-2.5 py-1 rounded-md bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300 text-xs font-medium border border-slate-200 dark:border-slate-700">
|
||||
{{ author }}
|
||||
@@ -255,7 +284,7 @@ const getTagClass = (id: number) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto min-w-[200px]">
|
||||
<div class="flex-1 md:w-auto min-w-[300px]">
|
||||
<label
|
||||
class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Tags</label>
|
||||
<div
|
||||
@@ -269,10 +298,10 @@ const getTagClass = (id: number) => {
|
||||
if (idx > -1) uploadForm.selectedTagIds.splice(idx, 1);
|
||||
else uploadForm.selectedTagIds.push(tag.id);
|
||||
}" :class="[
|
||||
uploadForm.selectedTagIds.includes(tag.id)
|
||||
? getTagClass(tag.id) + ' border-transparent'
|
||||
: 'bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400'
|
||||
]"
|
||||
uploadForm.selectedTagIds.includes(tag.id)
|
||||
? getTagClass(tag.id) + ' border-transparent'
|
||||
: 'bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400'
|
||||
]"
|
||||
class="px-3 py-1.5 rounded-full border text-xs font-bold uppercase tracking-tight transition-all shadow-sm whitespace-nowrap">
|
||||
{{ tag.name }}
|
||||
</button>
|
||||
@@ -286,7 +315,7 @@ const getTagClass = (id: number) => {
|
||||
<button :disabled="isSubmitting"
|
||||
class="inline-flex w-full justify-center rounded-lg bg-blue-600 px-6 py-2.5 text-sm font-bold text-white shadow-sm hover:bg-blue-500 disabled:opacity-50 sm:w-auto transition-all"
|
||||
@click="submitPublication">
|
||||
{{ isSubmitting ? 'Uploading...' : 'Submit Publication' }}
|
||||
{{ isSubmitting ? 'Uploading...' : 'Submit' }}
|
||||
</button>
|
||||
<button
|
||||
class="inline-flex w-full justify-center rounded-lg bg-white dark:bg-slate-800 px-6 py-2.5 text-sm font-bold text-slate-700 dark:text-slate-300 shadow-sm ring-1 ring-inset ring-slate-300 dark:ring-slate-700 hover:bg-slate-50 sm:w-auto transition-all"
|
||||
|
||||
Reference in New Issue
Block a user