-
{{ comment }}
+ v-for="(comment, cIndex) in article.comments" :key="cIndex"
+ dot-color="secondary" size="x-small">
+
+
+
+ mdi-thumb-up
+ {{ comment.likes }}
+ mdi-thumb-down
+ {{ comment.dislikes }}
+
+
+
{{ comment.content }}
+
Aucune suggestion pour cet article
-
-
-
-
-
-
+
-
-
-
-
-
- Signer la Charte
-
-
-
-
-
-
-
-
- mdi-check-circle
- Signer
-
-
+
+
+
+
-
+
+
+
+
+
+ Signer la Charte
+
+
+
+
+
+
+
+
+ mdi-check-circle
+ Signer
+
+
+
+
+
+
+
+
+
+
+
+
+
+ voir les signatures
+
+
+
+
+
+
+ {{ signature.name }} @{{ signature.createdAt }}
+
+
{{ signature.comment }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ overlay.icon }}
+ {{ overlay.message }}
+
+ {{ overlay.buttonText }}
+
-
+
-
-
-
- {{ overlayIcon }}
- {{ overlayMessage }}
-
- {{ overlayButtonText }}
-
-
-
@@ -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;
};
-
+
diff --git a/plugins/vuetify.ts b/plugins/vuetify.ts
deleted file mode 100644
index cea2820..0000000
--- a/plugins/vuetify.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-// import this after install `@mdi/font` package
-import '@mdi/font/css/materialdesignicons.css'
-
-import 'vuetify/styles'
-import { createVuetify } from 'vuetify'
-import { VBtn } from 'vuetify/components';
-
-export default defineNuxtPlugin((app) => {
- const vuetify = createVuetify({
- theme: {
- themes: {
- light: {
- colors: {
- primary: '#A7D129',
- secondary: '#686D76',
- tertiary: '#BE3737',
- tangerine: '#EC8F67ff',
- accent: '#000000',
- },
- },
- },
- },
- aliases: {
- VBtnValid: VBtn,
- },
- defaults: {
- VBtn: {
- color: 'accent',
- class: 'custom-btn',
- },
- VBtnValid: {
- color: 'primary',
- class: 'custom-btn',
- },
- },
- });
- app.vueApp.use(vuetify)
-})
diff --git a/prisma/migrations/20241222124646_dav/migration.sql b/prisma/migrations/20241222124646_dav/migration.sql
new file mode 100644
index 0000000..b1bdf29
--- /dev/null
+++ b/prisma/migrations/20241222124646_dav/migration.sql
@@ -0,0 +1,24 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `comments` on the `Article` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE "Article" DROP COLUMN "comments";
+
+-- CreateTable
+CREATE TABLE "Comment" (
+ "id" TEXT NOT NULL,
+ "content" TEXT NOT NULL,
+ "likes" INTEGER NOT NULL DEFAULT 0,
+ "dislikes" INTEGER NOT NULL DEFAULT 0,
+ "articleId" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "Comment_pkey" PRIMARY KEY ("id")
+);
+
+-- AddForeignKey
+ALTER TABLE "Comment" ADD CONSTRAINT "Comment_articleId_fkey" FOREIGN KEY ("articleId") REFERENCES "Article"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index d433da2..23cda75 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -8,14 +8,25 @@ datasource db {
}
model Article {
- id String @id @default(uuid())
+ id String @id @default(uuid())
title String
description String
- likes Int @default(0)
- dislikes Int @default(0)
- comments String[] @default([])
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
+ likes Int @default(0)
+ dislikes Int @default(0)
+ comments Comment[]
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
+
+model Comment {
+ id String @id @default(uuid())
+ content String
+ likes Int @default(0)
+ dislikes Int @default(0)
+ articleId String // Foreign key for the related article
+ article Article @relation(fields: [articleId], references: [id]) // Define relationship
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
}
model Charte {
diff --git a/public/robots.txt b/public/robots.txt
deleted file mode 100644
index 8b13789..0000000
--- a/public/robots.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/server/api/articles/[id].get.ts b/server/api/articles/[id].get.ts
index 0e547f7..61b5503 100644
--- a/server/api/articles/[id].get.ts
+++ b/server/api/articles/[id].get.ts
@@ -2,14 +2,18 @@ import prisma from '~/lib/prisma';
export default defineEventHandler(async (event) => {
const id = event.context.params?.id;
-console.log('prisma' + event.context.params?.id)
+
try {
- const article = await prisma.article.findUnique({ where: { id } });
+ const article = await prisma.article.findUnique({
+ where: { id },
+ include: { comments: true },
+ });
if (!article) {
return {
success: false,
- data: null,
+ data: null,
+ message: 'Article not found.',
};
}
diff --git a/server/api/articles/[id].put.ts b/server/api/articles/[id].put.ts
index befd860..6ab241c 100644
--- a/server/api/articles/[id].put.ts
+++ b/server/api/articles/[id].put.ts
@@ -4,7 +4,7 @@ export default defineEventHandler(async (event) => {
const id = event.context.params?.id;
const body = await readBody(event);
- if (!id ) {
+ if (!id) {
return {
success: false,
message: 'Invalid article ID.',
@@ -15,19 +15,19 @@ export default defineEventHandler(async (event) => {
const updatedArticle = await prisma.article.update({
where: { id },
data: {
- id: body.id,
title: body.title,
description: body.description,
likes: body.likes,
dislikes: body.dislikes,
- comments: body.comments,
},
});
+
return {
success: true,
data: updatedArticle,
};
} catch (error) {
+ console.error('Error updating article:', error);
return {
success: false,
error: error.message,
diff --git a/server/api/articles/index.get.ts b/server/api/articles/index.get.ts
index 1469863..c300920 100644
--- a/server/api/articles/index.get.ts
+++ b/server/api/articles/index.get.ts
@@ -2,12 +2,14 @@ import prisma from '~/lib/prisma';
export default defineEventHandler(async (event) => {
try {
- const articles = await prisma.article.findMany();
+ const articles = await prisma.article.findMany({
+ include: { comments: true },
+ });
if (!articles || articles.length === 0) {
return {
success: false,
- data: null,
+ data: null,
message: 'No articles found.',
};
}
diff --git a/server/api/articles/index.post.ts b/server/api/articles/index.post.ts
index 3d0b84a..b1472e9 100644
--- a/server/api/articles/index.post.ts
+++ b/server/api/articles/index.post.ts
@@ -3,16 +3,14 @@ import prisma from '~/lib/prisma';
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event);
- const { id, title, description, likes, dislikes, comments } = body;
+ const { title, description, likes, dislikes } = body;
const newArticle = await prisma.article.create({
data: {
- id,
title,
description,
likes: likes || 0,
dislikes: dislikes || 0,
- comments: comments || [],
},
});
diff --git a/server/api/charte/index.get.ts b/server/api/charte/index.get.ts
new file mode 100644
index 0000000..14270cd
--- /dev/null
+++ b/server/api/charte/index.get.ts
@@ -0,0 +1,26 @@
+import prisma from '~/lib/prisma';
+
+export default defineEventHandler(async (event) => {
+ try {
+ const chartes = await prisma.charte.findMany();
+
+ if (!chartes || chartes.length === 0) {
+ return {
+ success: false,
+ data: null,
+ message: 'No chartes found.',
+ };
+ }
+
+ return {
+ success: true,
+ data: chartes,
+ };
+ } catch (error) {
+ console.error('Error fetching chartes:', error);
+ return {
+ success: false,
+ error: error.message,
+ };
+ }
+});
diff --git a/server/api/comments/[id].delete.ts b/server/api/comments/[id].delete.ts
new file mode 100644
index 0000000..8bf7ac3
--- /dev/null
+++ b/server/api/comments/[id].delete.ts
@@ -0,0 +1,30 @@
+import prisma from '~/lib/prisma';
+
+export default defineEventHandler(async (event) => {
+ const id = event.context.params?.id;
+
+ if (!id) {
+ return {
+ success: false,
+ message: 'Invalid comment ID.',
+ };
+ }
+
+ try {
+ await prisma.comment.delete({
+ where: { id },
+ });
+
+ return {
+ success: true,
+ message: 'Comment deleted successfully',
+ };
+ } catch (error) {
+ console.error('Error deleting comment:', error);
+ return {
+ success: false,
+ message: 'An error occurred while deleting the comment',
+ error: error.message,
+ };
+ }
+});
diff --git a/server/api/comments/[id].put.ts b/server/api/comments/[id].put.ts
new file mode 100644
index 0000000..5592e16
--- /dev/null
+++ b/server/api/comments/[id].put.ts
@@ -0,0 +1,37 @@
+import prisma from '~/lib/prisma';
+
+export default defineEventHandler(async (event) => {
+ const id = event.context.params?.id;
+ const body = await readBody(event);
+
+ if (!id) {
+ return {
+ success: false,
+ message: 'Invalid comment ID.',
+ };
+ }
+
+ try {
+ const updatedComment = await prisma.comment.update({
+ where: { id },
+ data: {
+ content: body.content,
+ likes: body.likes,
+ dislikes: body.dislikes,
+ },
+ });
+
+ return {
+ success: true,
+ message: 'Comment updated successfully',
+ data: updatedComment,
+ };
+ } catch (error) {
+ console.error('Error updating comment:', error);
+ return {
+ success: false,
+ message: 'An error occurred while updating the comment',
+ error: error.message,
+ };
+ }
+});
diff --git a/server/api/comments/index.get.ts b/server/api/comments/index.get.ts
new file mode 100644
index 0000000..7ec3d98
--- /dev/null
+++ b/server/api/comments/index.get.ts
@@ -0,0 +1,24 @@
+import prisma from '~/lib/prisma';
+
+export default defineEventHandler(async (event) => {
+ const query = getQuery(event);
+ const { articleId } = query;
+
+ try {
+ const comments = await prisma.comment.findMany({
+ where: articleId ? { articleId: String(articleId) } : undefined,
+ });
+
+ return {
+ success: true,
+ data: comments,
+ };
+ } catch (error) {
+ console.error('Error fetching comments:', error);
+ return {
+ success: false,
+ message: 'An error occurred while fetching comments',
+ error: error.message,
+ };
+ }
+});
diff --git a/server/api/comments/index.post.ts b/server/api/comments/index.post.ts
new file mode 100644
index 0000000..0e7dc58
--- /dev/null
+++ b/server/api/comments/index.post.ts
@@ -0,0 +1,37 @@
+import prisma from '~/lib/prisma';
+
+export default defineEventHandler(async (event) => {
+ try {
+ const body = await readBody(event);
+ const { content, articleId, likes, dislikes } = body;
+
+ if (!content || !articleId) {
+ return {
+ success: false,
+ message: 'Content and articleId are required to create a comment.',
+ };
+ }
+
+ const newComment = await prisma.comment.create({
+ data: {
+ content,
+ articleId,
+ likes: likes || 0,
+ dislikes: dislikes || 0,
+ },
+ });
+
+ return {
+ success: true,
+ message: 'Comment created successfully',
+ data: newComment,
+ };
+ } catch (error) {
+ console.error('Error creating comment:', error);
+ return {
+ success: false,
+ message: 'An error occurred while creating the comment',
+ error: error.message,
+ };
+ }
+});