add interactions to comments
This commit is contained in:
719
pages/index.vue
719
pages/index.vue
@@ -1,197 +1,245 @@
|
||||
<template>
|
||||
<v-container fluid class="lg:mx-12 mx-2">
|
||||
<v-row class="video-section">
|
||||
<video autoplay loop muted playsinline class="video-background">
|
||||
<source src="/videos/video.mp4" type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<v-container fluid>
|
||||
<!-- Loading Section -->
|
||||
<v-row v-if="isLoading" class="justify-center">
|
||||
<Loader />
|
||||
</v-row>
|
||||
<!-- Introduction section -->
|
||||
<v-row v-if="introduction" class="d-flex justify-center align-center text-center">
|
||||
<h1 class="my-8 lg:text-xl text-l text-black">{{ introduction.title }}</h1>
|
||||
<p class="text-xs text-black mb-8 mr-2 lg:mr-8 text-justify">{{ introduction.description }}</p>
|
||||
</v-row>
|
||||
<!-- Articles Section -->
|
||||
<v-row>
|
||||
<!-- Left Column -->
|
||||
<v-col cols="12" md="6">
|
||||
<div v-for="(article, index) in leftColumnArticles" :key="article.id || index" class="article">
|
||||
<v-card class="mb-12" elevation="0">
|
||||
<v-card-title>
|
||||
<h2 class="lg:text-sm text-xs text-wrap text-black font-bold">{{ article.title }}</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="text-xs text-black">{{ article.description }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn icon="mdi-play" @click="toggleShow(article.id)">
|
||||
</v-btn>
|
||||
<p class="text-gray-700 lg:text-xs text-xs">Suggérer une modification de texte</p>
|
||||
</v-card-actions>
|
||||
|
||||
<v-expand-transition>
|
||||
<div v-show="showStates[article.id]">
|
||||
<v-divider></v-divider>
|
||||
<p class="text-gray-500 text-xs mx-5"> La constitution des DAV ne se fera pas
|
||||
seule. Chacun et
|
||||
chacune sont
|
||||
invité(e)s à donner le meilleur d’eux même pour assurer sa constitution,
|
||||
son respect, ainsi que sa mise en application.
|
||||
Soyez courtois . Tout commentaire désobligeant irrespectueux ou agressif sera
|
||||
systématiquement supprimé. </p>
|
||||
<!-- Main Content Section -->
|
||||
<v-row v-else class="mx-5">
|
||||
<v-row class="video-section">
|
||||
<video autoplay loop muted playsinline class="video-background">
|
||||
<source src="/videos/video.mp4" type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</v-row>
|
||||
<!-- Introduction section -->
|
||||
<v-row v-if="introduction" class="d-flex justify-center align-center text-center">
|
||||
<h1 class="my-8 lg:text-xl text-l text-black">{{ introduction.title }}</h1>
|
||||
<p class="text-xs text-black sm:mb-24 mr-2 lg:mr-8 text-justify">{{ introduction.description }}</p>
|
||||
</v-row>
|
||||
<!-- Articles Section -->
|
||||
<v-row>
|
||||
<!-- Left Column -->
|
||||
<v-col cols="12" md="6">
|
||||
<div v-for="(article, index) in leftColumnArticles" :key="article.id || index" class="article">
|
||||
<v-card class="mb-12" elevation="0">
|
||||
<v-card-title>
|
||||
<h2 class="lg:text-sm text-xs text-wrap text-black font-bold">{{ article.title }}</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="text-xs text-black">{{ article.description }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn icon="mdi-play" @click="toggleShow(article.id)">
|
||||
</v-btn>
|
||||
<p class="text-gray-700 lg:text-xs text-xs">Suggérer une modification de texte</p>
|
||||
</v-card-actions>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<v-card-text>
|
||||
<v-textarea v-model="newComments[article.id]" outlined rows="2"
|
||||
auto-grow></v-textarea>
|
||||
<VBtnValid @click="addComment(article.id)">
|
||||
Valider
|
||||
</VBtnValid>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<!-- Right Column -->
|
||||
<v-col cols="12" md="6">
|
||||
<div v-for="(article, index) in rightColumnArticles" :key="article.id || index" class="article">
|
||||
<v-card class="mb-12" elevation="0">
|
||||
<v-card-title>
|
||||
<h2 class="lg:text-sm text-xs text-wrap text-black font-bold">{{ article.title }}</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="text-xs text-black">{{ article.description }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn icon="mdi-play" @click="toggleShow(article.id)">
|
||||
</v-btn>
|
||||
<p class="text-gray-700 lg:text-xs text-xs">Suggérer une modification de texte</p>
|
||||
</v-card-actions>
|
||||
|
||||
<v-expand-transition>
|
||||
<div v-show="showStates[article.id]">
|
||||
<v-divider></v-divider>
|
||||
<p class="text-gray-500 text-xs mx-5"> La constitution des DAV ne se fera pas
|
||||
seule. Chacun et
|
||||
chacune sont
|
||||
invité(e)s à donner le meilleur d’eux même pour assurer sa constitution,
|
||||
son respect, ainsi que sa mise en application.
|
||||
Soyez courtois . Tout commentaire désobligeant irrespectueux ou agressif sera
|
||||
systématiquement supprimé. </p>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<v-card-text>
|
||||
<v-textarea v-model="newComments[article.id]" outlined rows="2"
|
||||
auto-grow></v-textarea>
|
||||
<VBtnValid @click="addComment(article.id)">
|
||||
Valider
|
||||
</VBtnValid>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="summary" class="d-flex justify-center align-center text-center">
|
||||
<p class="text-xs text-black mb-8 px-1 text-center">{{ summary.description }}</p>
|
||||
</v-row>
|
||||
<v-row class="justify-center">
|
||||
<v-col cols="12" md="6" lg="8"> <!-- Make it take more space on large screens -->
|
||||
<v-expansion-panels variant="accordion">
|
||||
<v-expansion-panel v-for="(article, index) in articles" :key="article.id" class="article">
|
||||
<v-expansion-panel-title class="d-flex flex-column align-start" expand-icon="" collapse-icon="">
|
||||
<div class="d-flex align-center mb-2">
|
||||
<span class="mr-2">
|
||||
<v-icon class="m-1 text-primary hover:text-green-500 text-xs"
|
||||
@click.stop="likeArticle(article.id)">
|
||||
mdi-thumb-up
|
||||
</v-icon>
|
||||
<p class="ml-2 lg:ml-0 text-primary">{{ article.likes }}</p>
|
||||
</span>
|
||||
|
||||
<span class="mr-3">
|
||||
<v-icon class="m-1 text-tertiary hover:text-red-500 text-xs"
|
||||
@click.stop="dislikeArticle(article.id)">
|
||||
mdi-thumb-down
|
||||
</v-icon>
|
||||
<p class="ml-2 lg:ml-0 text-tertiary">{{ article.dislikes }}</p>
|
||||
</span>
|
||||
<h2 class="text-sm text-wrap text-black font-bold m-0">
|
||||
{{ article.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="d-flex align-center mt-2">
|
||||
<!-- Control the panel opening -->
|
||||
<v-icon class="mr-1">
|
||||
mdi-play
|
||||
</v-icon>
|
||||
<p class="text-gray-700 text-xs ml-2">Voir les suggestions</p>
|
||||
</div>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-expand-transition>
|
||||
<div v-show="showStates[article.id]">
|
||||
<v-divider></v-divider>
|
||||
<p class="text-gray-500 text-xs mx-5"> La constitution des DAV ne se fera pas
|
||||
seule. Chacun et
|
||||
chacune sont
|
||||
invité(e)s à donner le meilleur d’eux même pour assurer sa constitution,
|
||||
son respect, ainsi que sa mise en application.
|
||||
Soyez courtois . Tout commentaire désobligeant irrespectueux ou agressif sera
|
||||
systématiquement supprimé. </p>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<v-card-text>
|
||||
<v-textarea v-model="newComments[article.id]" outlined rows="2"
|
||||
auto-grow></v-textarea>
|
||||
<VBtnValid @click="addComment(article.id)">
|
||||
Valider
|
||||
</VBtnValid>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<!-- Right Column -->
|
||||
<v-col cols="12" md="6">
|
||||
<div v-for="(article, index) in rightColumnArticles" :key="article.id || index" class="article">
|
||||
<v-card class="mb-12" elevation="0">
|
||||
<v-card-title>
|
||||
<h2 class="lg:text-sm text-xs text-wrap text-black font-bold">{{ article.title }}</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="text-xs text-black">{{ article.description }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn icon="mdi-play" @click="toggleShow(article.id)">
|
||||
</v-btn>
|
||||
<p class="text-gray-700 lg:text-xs text-xs">Suggérer une modification de texte</p>
|
||||
</v-card-actions>
|
||||
|
||||
<v-expand-transition>
|
||||
<div v-show="showStates[article.id]">
|
||||
<v-divider></v-divider>
|
||||
<p class="text-gray-500 text-xs mx-5"> La constitution des DAV ne se fera pas
|
||||
seule. Chacun et
|
||||
chacune sont
|
||||
invité(e)s à donner le meilleur d’eux même pour assurer sa constitution,
|
||||
son respect, ainsi que sa mise en application.
|
||||
Soyez courtois . Tout commentaire désobligeant irrespectueux ou agressif sera
|
||||
systématiquement supprimé. </p>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<v-card-text>
|
||||
<v-textarea v-model="newComments[article.id]" outlined rows="2"
|
||||
auto-grow></v-textarea>
|
||||
<VBtnValid @click="addComment(article.id)">
|
||||
Valider
|
||||
</VBtnValid>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="summary" class="d-flex justify-center align-center text-center">
|
||||
<p class="text-xs text-black mb-8 px-1 text-center">{{ summary.description }}</p>
|
||||
</v-row>
|
||||
<v-row class="justify-center">
|
||||
<v-col cols="12" md="6" lg="8"> <!-- Make it take more space on large screens -->
|
||||
<v-expansion-panels variant="accordion">
|
||||
<v-expansion-panel v-for="(article, index) in articles" :key="article.id" class="article">
|
||||
<v-expansion-panel-title class="d-flex flex-column align-start" expand-icon=""
|
||||
collapse-icon="">
|
||||
<div class="flex items-center mb-0">
|
||||
<!-- Like/Dislike Section -->
|
||||
<div class="mr-3 flex items-center">
|
||||
<v-icon class="mr-1 text-primary text-xs" size="18"
|
||||
@click.stop="likeArticle(article.id)">
|
||||
mdi-thumb-up
|
||||
</v-icon>
|
||||
<p class="text-primary text-sm">{{ article.likes }}</p>
|
||||
|
||||
<v-icon class="mr-1 ml-2 text-tertiary text-xs" size="18"
|
||||
@click.stop="dislikeArticle(article.id)">
|
||||
mdi-thumb-down
|
||||
</v-icon>
|
||||
<p class="text-tertiary text-sm">{{ article.dislikes }}</p>
|
||||
</div>
|
||||
<h2 class="text-sm text-wrap text-black font-bold m-0">
|
||||
{{ article.title }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="d-flex align-center mt-2">
|
||||
<!-- Control the panel opening -->
|
||||
<v-icon class="mr-1">
|
||||
mdi-play
|
||||
</v-icon>
|
||||
<p class="text-gray-700 text-xs ml-2">Voir les suggestions</p>
|
||||
</div>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-timeline align="start" density="compact" class="ml-3">
|
||||
<v-timeline-item v-if="article.comments.length"
|
||||
v-for="(comment, cIndex) in article.comments" :key="cIndex" dot-color="primary"
|
||||
size="x-small">
|
||||
<div class="mb-0">
|
||||
<p class="text-sm text-black">{{ comment }}</p>
|
||||
v-for="(comment, cIndex) in article.comments" :key="cIndex"
|
||||
dot-color="secondary" size="x-small">
|
||||
<div class="flex items-center mb-0">
|
||||
<!-- Like/Dislike Section -->
|
||||
<div class="mr-3 flex items-center">
|
||||
<v-icon color="primary" size="14"
|
||||
@click="likeComment(article.id, comment.id)">mdi-thumb-up</v-icon>
|
||||
<span class="text-xs text-primary ml-1 mr-2">{{ comment.likes }}</span>
|
||||
<v-icon color="tertiary" size="14"
|
||||
@click="dislikeComment(article.id, comment.id)">mdi-thumb-down</v-icon>
|
||||
<span class="text-xs text-tertiary ml-1">{{ comment.dislikes }}</span>
|
||||
</div>
|
||||
<!-- Comment Content -->
|
||||
<p class="text-sm text-black">{{ comment.content }}</p>
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
|
||||
<!-- No Comments Placeholder -->
|
||||
<v-timeline-item v-else dot-color="secondary" size="x-small">
|
||||
<p class="text-sm text-black">Aucune suggestion pour cet article</p>
|
||||
</v-timeline-item>
|
||||
</v-timeline>
|
||||
</v-expand-transition>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-expansion-panel-text>
|
||||
|
||||
<!-- Signature Section -->
|
||||
<v-row class="justify-center my-8">
|
||||
<v-col cols="12" md="8" lg="6">
|
||||
<v-card elevation="3" class="pa-6 bg-gray-100" color="secondary">
|
||||
<v-card-title class="text-center">
|
||||
<h2 class="text-uppercase text-md font-semibold font-sans">Signer la Charte</h2>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-form @submit.prevent="submitSignature" lazy-validation class="text-xs font-sans">
|
||||
<v-text-field v-model="name" label="Nom complet" outlined dense hide-details
|
||||
:rules="[rules.required]" class="mb-2" prepend-inner-icon="mdi-account"></v-text-field>
|
||||
<v-text-field v-model="email" type="email" label="Email" placeholder="Entrez votre email"
|
||||
outlined dense hide-details :rules="[rules.required, rules.email]" class="mb-2"
|
||||
prepend-inner-icon="mdi-email"></v-text-field>
|
||||
<v-textarea v-model="comment" label="Ajouter un commentaire"
|
||||
placeholder="Partagez vos réflexions ou suggestions" outlined dense hide-details
|
||||
rows="2" class="mb-2" prepend-inner-icon="mdi-message-text"></v-textarea>
|
||||
<VBtnValid :disabled="!isFormValid" type="submit" block>
|
||||
<v-icon size="16" left>mdi-check-circle</v-icon>
|
||||
Signer
|
||||
</VBtnValid>
|
||||
</v-form>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
</v-card-text>
|
||||
<!-- Signature Section -->
|
||||
<v-row class="mr-2">
|
||||
<v-col cols="12">
|
||||
<v-card elevation="3" class="pa-6 bg-gray-100" color="secondary">
|
||||
<v-card-title class="text-center">
|
||||
<h2 class="text-uppercase text-md font-semibold font-sans">Signer la Charte</h2>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-form @submit.prevent="submitSignature" lazy-validation class="text-xs font-sans">
|
||||
<v-text-field v-model="name" label="Nom / prénom / pseudonyme" outlined dense
|
||||
hide-details :rules="[rules.required]" class="mb-2"
|
||||
prepend-inner-icon="mdi-account"></v-text-field>
|
||||
<v-text-field v-model="email" type="email" label="Email"
|
||||
placeholder="Entrez votre email" outlined dense hide-details
|
||||
:rules="[rules.required, rules.email]" class="mb-2"
|
||||
prepend-inner-icon="mdi-email"></v-text-field>
|
||||
<v-textarea v-model="comment" label="Ajouter un commentaire"
|
||||
placeholder="Partagez vos réflexions ou suggestions" outlined dense hide-details
|
||||
rows="2" class="mb-2" prepend-inner-icon="mdi-message-text"></v-textarea>
|
||||
<VBtnValid type="submit" block>
|
||||
<v-icon size="16" left>mdi-check-circle</v-icon>
|
||||
Signer
|
||||
</VBtnValid>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Signature Display Section -->
|
||||
<v-row class=" sm:my-8">
|
||||
<v-col cols="12">
|
||||
<v-expansion-panels variant="accordion">
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title class="d-flex flex-column align-start" expand-icon=""
|
||||
color="primary" collapse-icon="">
|
||||
<div class="text-uppercase text-l font-semibold font-sans">voir les signatures</div>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-timeline align="start" density="compact">
|
||||
<v-timeline-item v-for="signature in signatures" :key="signature.id"
|
||||
dot-color="secondary" size="x-small">
|
||||
<div>
|
||||
<div class="font-weight-normal font-sans text-sm">
|
||||
<strong>{{ signature.name }}</strong> @{{ signature.createdAt }}
|
||||
</div>
|
||||
<div class="font-weight-normal font-sans text-sm">{{ signature.comment }}
|
||||
</div>
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
</v-timeline>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Success Overlay (Dynamic Content) -->
|
||||
<v-overlay v-model="overlay.visible" class="d-flex align-center justify-center"
|
||||
:style="{ background: 'rgba(0, 0, 0, 0.5)', backdropFilter: 'blur(4px)' }">
|
||||
<v-card class="py-10 px-8 text-center" elevation="4" rounded="lg" style="max-width: 400px;">
|
||||
<v-icon :color="overlay.iconColor" size="48" class="mb-4">{{ overlay.icon }}</v-icon>
|
||||
<p class="overlay-msg text-sm font-semibold text-black text-uppercase">{{ overlay.message }}</p>
|
||||
<VBtnValid class="mt-6" @click="overlay.visible = false">
|
||||
{{ overlay.buttonText }}
|
||||
</VBtnValid>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-overlay>
|
||||
</v-row>
|
||||
<!-- Success Overlay (Dynamic Content) -->
|
||||
<v-overlay v-model="overlay" class="d-flex align-center justify-center"
|
||||
:style="{ background: 'rgba(0, 0, 0, 0.5)', backdropFilter: 'blur(4px)' }">
|
||||
<v-card class="py-10 px-8 text-center" elevation="4" rounded="lg" style="max-width: 400px;">
|
||||
<v-icon :color="overlayIconColor" size="48" class="mb-4">{{ overlayIcon }}</v-icon>
|
||||
<p class="overlay-msg text-sm font-semibold text-black text-uppercase">{{ overlayMessage }}</p>
|
||||
<VBtnValid class="mt-6" @click="overlay = false">
|
||||
{{ overlayButtonText }}
|
||||
</VBtnValid>
|
||||
</v-card>
|
||||
</v-overlay>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
@@ -199,6 +247,11 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useAsyncData } from 'nuxt/app';
|
||||
|
||||
import Loader from '~/components/Loader.vue';
|
||||
import { updateLikeDislike } from '~/lib/likes.ts';
|
||||
import { fetchArticlesData, fetchCommentsData, fetchSignatures } from '~/lib/fetchers';
|
||||
import { showOverlay } from '~/lib/overlayHandler';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'default',
|
||||
});
|
||||
@@ -210,7 +263,6 @@ const rightColumnArticles = ref([]);
|
||||
const showStates = ref({});
|
||||
const introduction = ref(null);
|
||||
const summary = ref(null);
|
||||
|
||||
const signatures = ref([]);
|
||||
// Form fields
|
||||
const name = ref('');
|
||||
@@ -218,12 +270,15 @@ const email = ref('');
|
||||
const comment = ref('');
|
||||
|
||||
const isFormValid = computed(() => name.value && email.value);
|
||||
const isLoading = ref(true);
|
||||
|
||||
const overlay = ref(false);
|
||||
const overlayIcon = ref('mdi-check-circle'); // Default icon
|
||||
const overlayMessage = ref('Votre suggestion à été ajoutée avec succès'); // Default message
|
||||
const overlayButtonText = ref('Continuer'); // Default button text
|
||||
const overlayIconColor = ref('success');
|
||||
const overlay = ref({
|
||||
icon: '',
|
||||
message: '',
|
||||
buttonText: '',
|
||||
iconColor: '',
|
||||
visible: false,
|
||||
})
|
||||
|
||||
const rules = {
|
||||
required: (value) => !!value || "Ce champ est obligatoire.",
|
||||
@@ -235,178 +290,190 @@ const toggleShow = (articleId) => {
|
||||
};
|
||||
|
||||
// Fetch articles data
|
||||
const { data: contentData, error } = await useAsyncData("content", () =>
|
||||
queryContent().find()
|
||||
);
|
||||
const fetchContent = async () => {
|
||||
try {
|
||||
const { data: contentData, error } = await useAsyncData('content', () =>
|
||||
queryContent().find()
|
||||
);
|
||||
|
||||
// Process fetched content
|
||||
if (!error.value && contentData.value) {
|
||||
for (const file of contentData.value) {
|
||||
if (file.type === "intro") {
|
||||
introduction.value = file;
|
||||
} else if (file.type === "article") {
|
||||
try {
|
||||
// Add default values (likes, dislikes, comments) if missing
|
||||
const article = {
|
||||
id: file.id,
|
||||
title: file.title,
|
||||
description: file.description,
|
||||
likes: file.likes || 0,
|
||||
dislikes: file.dislikes || 0,
|
||||
comments: file.comments || [],
|
||||
};
|
||||
// Process fetched content
|
||||
if (!error.value && contentData.value) {
|
||||
for (const file of contentData.value) {
|
||||
if (file.type === "intro") {
|
||||
introduction.value = file;
|
||||
} else if (file.type === "article") {
|
||||
const article = {
|
||||
id: file.id,
|
||||
title: file.title,
|
||||
description: file.description,
|
||||
likes: file.likes || 0,
|
||||
dislikes: file.dislikes || 0,
|
||||
comments: file.comments || [],
|
||||
};
|
||||
|
||||
// Add the article locally
|
||||
articles.value.push(article);
|
||||
// Add the article locally
|
||||
articles.value.push(article);
|
||||
|
||||
await $fetch('/api/articles', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
id: article.id,
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
likes: article.likes,
|
||||
dislikes: article.dislikes,
|
||||
comments: article.comments,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error creating article:', err);
|
||||
await $fetch('/api/articles', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
id: article.id,
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
likes: article.likes,
|
||||
dislikes: article.dislikes
|
||||
},
|
||||
});
|
||||
} else if (file.type === "summary") {
|
||||
summary.value = file;
|
||||
}
|
||||
}
|
||||
} else if (file.type === "summary") {
|
||||
summary.value = file;
|
||||
}
|
||||
// Split articles into left and right columns
|
||||
leftColumnArticles.value = articles.value.filter((_, index) => index % 2 === 0);
|
||||
rightColumnArticles.value = articles.value.filter((_, index) => index % 2 !== 0);
|
||||
// Initialize newComments object based on article IDs
|
||||
articles.value.forEach(article => {
|
||||
newComments.value[article.id] = '';
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize newComments object based on article IDs
|
||||
articles.value.forEach(article => {
|
||||
newComments.value[article.id] = '';
|
||||
});
|
||||
|
||||
// Split articles into two columns (assuming a 50% split)
|
||||
const midIndex = Math.ceil(articles.value.length / 2);
|
||||
leftColumnArticles.value = articles.value.slice(0, midIndex);
|
||||
rightColumnArticles.value = articles.value.slice(midIndex);
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Error fetching content:', err);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const response = await $fetch(`/api/articles`, {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (response.success && response.data) {
|
||||
// Update the articles array by merging data from the response
|
||||
articles.value = articles.value.map((article) => {
|
||||
const updatedArticle = response.data.find((data) => data.id === article.id);
|
||||
return updatedArticle
|
||||
? {
|
||||
...article,
|
||||
likes: updatedArticle.likes,
|
||||
dislikes: updatedArticle.dislikes,
|
||||
comments: updatedArticle.comments,
|
||||
}
|
||||
: article;
|
||||
});
|
||||
} else {
|
||||
console.error('Failed to fetch articles:', response.message || 'Unknown error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching articles:', error);
|
||||
}
|
||||
await fetchContent()
|
||||
await fetchArticlesData(articles);
|
||||
await fetchCommentsData(articles);
|
||||
await fetchSignatures(signatures);
|
||||
});
|
||||
|
||||
const likeArticle = (articleId) => {
|
||||
updateLikeDislike({
|
||||
type: 'article',
|
||||
action: 'like',
|
||||
id: articleId,
|
||||
articles,
|
||||
});
|
||||
};
|
||||
|
||||
const likeArticle = async (articleId) => {
|
||||
const article = articles.value.find((a) => a.id === articleId);
|
||||
if (!article) return;
|
||||
article.likes += 1;
|
||||
const dislikeArticle = (articleId) => {
|
||||
updateLikeDislike({
|
||||
type: 'article',
|
||||
action: 'dislike',
|
||||
id: articleId,
|
||||
articles,
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
await $fetch(`/api/articles/${articleId}`, {
|
||||
method: 'PUT',
|
||||
body: { likes: article.likes },
|
||||
});
|
||||
const likeComment = (articleId, commentId) => {
|
||||
updateLikeDislike({
|
||||
type: 'comment',
|
||||
action: 'like',
|
||||
id: commentId,
|
||||
articleId,
|
||||
articles,
|
||||
});
|
||||
};
|
||||
|
||||
console.log('Article updated successfully');
|
||||
} catch (error) {
|
||||
console.error('Error in likeArticle:', error);
|
||||
}
|
||||
};;
|
||||
|
||||
|
||||
const dislikeArticle = async (articleId) => {
|
||||
const article = articles.value.find((a) => a.id === articleId);
|
||||
if (!article) return;
|
||||
article.dislikes += 1;
|
||||
|
||||
try {
|
||||
await $fetch(`/api/articles/${articleId}`, {
|
||||
method: 'PUT',
|
||||
body: { dislikes: article.dislikes },
|
||||
});
|
||||
|
||||
console.log('Article updated successfully');
|
||||
} catch (error) {
|
||||
console.error('Error in dislikeArticle:', error);
|
||||
}
|
||||
const dislikeComment = (articleId, commentId) => {
|
||||
updateLikeDislike({
|
||||
type: 'comment',
|
||||
action: 'dislike',
|
||||
id: commentId,
|
||||
articleId,
|
||||
articles,
|
||||
});
|
||||
};
|
||||
|
||||
const addComment = async (articleId) => {
|
||||
const commentText = newComments.value[articleId]?.trim();
|
||||
if (!commentText) return;
|
||||
|
||||
const article = articles.value.find((a) => a.id === articleId);
|
||||
article.comments.push(commentText);
|
||||
newComments.value[articleId] = '';
|
||||
|
||||
overlayIcon.value = 'mdi-check-circle';
|
||||
overlayMessage.value = `Merci pour votre suggestion. Veuillez la consulter tout en descendant vers le bas de la page`;
|
||||
overlayButtonText.value = 'Continuer';
|
||||
overlayIconColor.value = 'success';
|
||||
overlay.value = true;
|
||||
try {
|
||||
await $fetch(`/api/articles/${article.id}`, {
|
||||
method: 'PUT',
|
||||
body: { comments: article.comments },
|
||||
const response = await $fetch('/api/comments', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
content: commentText,
|
||||
articleId,
|
||||
likes: 0,
|
||||
dislikes: 0,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Comment added successfully');
|
||||
if (response.success) {
|
||||
const article = articles.value.find((a) => a.id === articleId);
|
||||
if (article) {
|
||||
article.comments.push({
|
||||
id: response.data.id,
|
||||
content: commentText,
|
||||
likes: 0,
|
||||
dislikes: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the input field for the current article
|
||||
newComments.value[articleId] = '';
|
||||
|
||||
// Display success overlay
|
||||
showOverlay(
|
||||
{
|
||||
icon: 'mdi-check-circle',
|
||||
message: `Merci pour votre suggestion. Veuillez la consulter tout en descendant vers le bas de la page.`,
|
||||
buttonText: 'Continuer',
|
||||
iconColor: 'success',
|
||||
},
|
||||
overlay
|
||||
);
|
||||
console.log('Comment added successfully');
|
||||
} else {
|
||||
console.error('Failed to add comment:', response.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in addComment:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const submitSignature = async () => {
|
||||
const signatureData = {
|
||||
name: name.value,
|
||||
email: email.value,
|
||||
comment: comment.value,
|
||||
};
|
||||
signatures.value.push({ ...signatureData });
|
||||
try {
|
||||
await $fetch('/api/charte', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(signatureData)
|
||||
})
|
||||
console.log('Charte is submitted successfully');
|
||||
} catch (error) {
|
||||
console.error('Error in submit signature:', error);
|
||||
}
|
||||
overlayIcon.value = 'mdi-check-circle';
|
||||
overlayMessage.value = `Merci pour votre signature ${name.value}`
|
||||
overlayButtonText.value = 'Continuer';
|
||||
overlayIconColor.value = 'success';
|
||||
overlay.value = true;
|
||||
name.value = '';
|
||||
email.value = '';
|
||||
comment.value = '';
|
||||
if (isFormValid.value) {
|
||||
const signatureData = {
|
||||
name: name.value,
|
||||
email: email.value,
|
||||
comment: comment.value,
|
||||
};
|
||||
signatures.value.push({ ...signatureData });
|
||||
try {
|
||||
await $fetch('/api/charte', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(signatureData)
|
||||
})
|
||||
// Display success overlay using the reusable function
|
||||
showOverlay(
|
||||
{
|
||||
icon: 'mdi-check-circle',
|
||||
message: `Merci pour votre signature ${name.value}`,
|
||||
buttonText: 'Continuer',
|
||||
iconColor: 'success',
|
||||
},
|
||||
overlay
|
||||
)
|
||||
name.value = '';
|
||||
email.value = '';
|
||||
comment.value = '';
|
||||
console.log('Charte is submitted successfully');
|
||||
} catch (error) {
|
||||
console.error('Error in submit signature:', error);
|
||||
}
|
||||
} return;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.video-section {
|
||||
position: relative;
|
||||
@@ -451,4 +518,8 @@ p {
|
||||
min-width: 40px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.v-icon:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user