Compare commits
1 Commits
feature/ui
...
0e23d7f455
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e23d7f455 |
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"Charte",
|
||||||
|
"Nuxt",
|
||||||
|
"supabase"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container style="height: 600px;">
|
|
||||||
<v-row align-content="center" class="fill-height" justify="center">
|
|
||||||
<v-col class="text-xl text-center text-bold text-uppercase font-sans " cols="12">
|
|
||||||
Soyez le bienvenue sur DAV
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="6">
|
|
||||||
<v-progress-linear color="primary" height="6" indeterminate
|
|
||||||
rounded></v-progress-linear>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Fetch and update articles data with likes and dislikes.
|
|
||||||
* @param {Ref<Array>} articles - Reactive array of articles.
|
|
||||||
*/
|
|
||||||
export const fetchArticlesData = async (articles) => {
|
|
||||||
try {
|
|
||||||
const response = await $fetch(`/api/articles`, { method: 'GET' });
|
|
||||||
|
|
||||||
if (response.success && response.data) {
|
|
||||||
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 }
|
|
||||||
: article;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('Failed to fetch articles:', response.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching articles data:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch and attach comments to their respective articles.
|
|
||||||
* @param {Ref<Array>} articles - Reactive array of articles.
|
|
||||||
*/
|
|
||||||
export const fetchCommentsData = async (articles) => {
|
|
||||||
try {
|
|
||||||
const response = await $fetch(`/api/comments`, { method: 'GET' });
|
|
||||||
|
|
||||||
if (response.success && response.data) {
|
|
||||||
articles.value = articles.value.map((article) => {
|
|
||||||
const relatedComments = response.data.filter((data) => data.articleId === article.id);
|
|
||||||
return { ...article, comments: relatedComments };
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('Failed to fetch comments:', response.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching comments data:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch and format signatures data.
|
|
||||||
* @param {Ref<Array>} signatures - Reactive array of signatures.
|
|
||||||
*/
|
|
||||||
export const fetchSignatures = async (signatures) => {
|
|
||||||
try {
|
|
||||||
const response = await $fetch(`/api/charte`, { method: 'GET' });
|
|
||||||
|
|
||||||
if (response.success && Array.isArray(response.data)) {
|
|
||||||
signatures.value = response.data.map((signature) => ({
|
|
||||||
...signature,
|
|
||||||
createdAt: new Intl.DateTimeFormat('en-US', {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
day: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
}).format(new Date(signature.createdAt)),
|
|
||||||
}));
|
|
||||||
} else if (response.success) {
|
|
||||||
console.error('Unexpected data format:', response.data);
|
|
||||||
} else {
|
|
||||||
console.error('Failed to fetch signatures:', response.message || 'Unknown error');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching signatures data:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
54
lib/likes.ts
54
lib/likes.ts
@@ -1,54 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Updates likes or dislikes for an article or comment dynamically.
|
|
||||||
* @param {string} type - 'article' or 'comment'.
|
|
||||||
* @param {string} action - 'like' or 'dislike'.
|
|
||||||
* @param {string} id - ID of the article or comment.
|
|
||||||
* @param {string} articleId - (Optional) ID of the parent article if updating a comment.
|
|
||||||
* @param {Array} articles - The articles array.
|
|
||||||
*/
|
|
||||||
export const updateLikeDislike = async ({ type, action, id, articleId = null, articles }) => {
|
|
||||||
try {
|
|
||||||
// Find the target item
|
|
||||||
let target;
|
|
||||||
if (type === 'article') {
|
|
||||||
target = articles.value.find((a) => a.id === id);
|
|
||||||
} else if (type === 'comment') {
|
|
||||||
const article = articles.value.find((a) => a.id === articleId);
|
|
||||||
if (article) {
|
|
||||||
target = article.comments.find((c) => c.id === id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!target) {
|
|
||||||
console.error(`Target ${type} not found`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the likes or dislikes
|
|
||||||
if (action === 'like') {
|
|
||||||
target.likes += 1;
|
|
||||||
} else if (action === 'dislike') {
|
|
||||||
target.dislikes += 1;
|
|
||||||
} else {
|
|
||||||
console.error('Invalid action specified');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine API endpoint
|
|
||||||
const endpoint = type === 'article' ? `/api/articles/${id}` : `/api/comments/${id}`;
|
|
||||||
|
|
||||||
// Make the API call
|
|
||||||
await $fetch(endpoint, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: {
|
|
||||||
[action === 'like' ? 'likes' : 'dislikes']: action === 'like' ? target.likes : target.dislikes,
|
|
||||||
...(type === 'comment' && { articleId }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`${type} ${action} updated successfully`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error updating ${type} ${action}:`, error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Display overlay with dynamic parameters.
|
|
||||||
* @param {Object} options - Overlay configuration.
|
|
||||||
* @param {string} options.icon - Icon to display in the overlay.
|
|
||||||
* @param {string} options.message - Message to display in the overlay.
|
|
||||||
* @param {string} options.buttonText - Button text to display in the overlay.
|
|
||||||
* @param {string} options.iconColor - Icon color to display in the overlay.
|
|
||||||
* @param {Ref<boolean>} overlay - Reactive overlay state.
|
|
||||||
*/
|
|
||||||
export const showOverlay = (options, overlay) => {
|
|
||||||
const { icon, message, buttonText, iconColor } = options;
|
|
||||||
|
|
||||||
overlay.value = {
|
|
||||||
icon,
|
|
||||||
message,
|
|
||||||
buttonText,
|
|
||||||
iconColor,
|
|
||||||
visible: true,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,31 +1,29 @@
|
|||||||
import { defineNuxtConfig } from 'nuxt/config';
|
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
ssr: false,
|
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2024-11-01',
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
app: {
|
|
||||||
head: {
|
|
||||||
title: 'DAV',
|
|
||||||
meta: [
|
|
||||||
{
|
|
||||||
name: 'description',
|
|
||||||
content: `Droits de l'ame et du vivant`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
css: [
|
css: [
|
||||||
'~/assets/css/main.css',
|
'~/assets/css/main.css',
|
||||||
],
|
],
|
||||||
|
|
||||||
postcss: {
|
postcss: {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
transpile: ['vuetify'],
|
||||||
|
},
|
||||||
modules: [
|
modules: [
|
||||||
'vuetify-nuxt-module',
|
(_options, nuxt) => {
|
||||||
|
nuxt.hooks.hook('vite:extendConfig', (config) => {
|
||||||
|
// @ts-expect-error
|
||||||
|
config.plugins.push(vuetify({ autoImport: true }))
|
||||||
|
})
|
||||||
|
},
|
||||||
'@nuxt/content',
|
'@nuxt/content',
|
||||||
'@nuxtjs/supabase',
|
'@nuxtjs/supabase',
|
||||||
"@prisma/nuxt"
|
"@prisma/nuxt"
|
||||||
@@ -36,36 +34,12 @@ export default defineNuxtConfig({
|
|||||||
key: process.env.SUPABASE_KEY,
|
key: process.env.SUPABASE_KEY,
|
||||||
redirect: false,
|
redirect: false,
|
||||||
},
|
},
|
||||||
vuetify: {
|
|
||||||
vuetifyOptions: {
|
|
||||||
components: 'VBtn',
|
|
||||||
theme: {
|
|
||||||
themes: {
|
|
||||||
light: {
|
|
||||||
colors: {
|
|
||||||
primary: '#A7D129',
|
|
||||||
secondary: '#FFD93D',
|
|
||||||
tertiary: '#FFB400',
|
|
||||||
accent: '#646464',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
aliases: {
|
|
||||||
VBtnValid: 'VBtn',
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
VBtn: {
|
|
||||||
color: 'accent',
|
|
||||||
class: 'custom-btn',
|
|
||||||
},
|
|
||||||
VBtnValid: {
|
|
||||||
color: 'primary',
|
|
||||||
class: 'custom-btn',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vite: {
|
vite: {
|
||||||
|
vue: {
|
||||||
|
template: {
|
||||||
|
transformAssetUrls,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
5780
package-lock.json
generated
5780
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -10,20 +10,22 @@
|
|||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mdi/font": "^7.4.47",
|
||||||
"@nuxt/content": "^2.13.4",
|
"@nuxt/content": "^2.13.4",
|
||||||
"@nuxtjs/supabase": "^1.4.4",
|
"@nuxtjs/supabase": "^1.4.4",
|
||||||
"@prisma/nuxt": "^0.1.3",
|
"@prisma/nuxt": "^0.1.3",
|
||||||
"nuxt": "^3.14.1592",
|
"nuxt": "^3.14.1592",
|
||||||
|
"uuid": "^11.0.3",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest",
|
"vue-router": "latest"
|
||||||
"vuetify": "^3.7.6",
|
|
||||||
"vuetify-nuxt-module": "^0.18.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@prisma/client": "^5.22.0",
|
"@prisma/client": "^5.22.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"prisma": "^6.1.0",
|
"prisma": "^6.0.1",
|
||||||
"tailwindcss": "^3.4.16"
|
"tailwindcss": "^3.4.16",
|
||||||
|
"vite-plugin-vuetify": "^2.0.4",
|
||||||
|
"vuetify": "^3.7.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
333
pages/index.vue
333
pages/index.vue
@@ -1,12 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container fluid>
|
<v-container fluid class="lg:mx-12 mx-2">
|
||||||
<!-- Loading Section -->
|
|
||||||
<v-row v-if="isLoading" class="justify-center">
|
|
||||||
<Loader />
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<!-- Main Content Section -->
|
|
||||||
<v-row v-else class="mx-5">
|
|
||||||
<v-row class="video-section">
|
<v-row class="video-section">
|
||||||
<video autoplay loop muted playsinline class="video-background">
|
<video autoplay loop muted playsinline class="video-background">
|
||||||
<source src="/videos/video.mp4" type="video/mp4" />
|
<source src="/videos/video.mp4" type="video/mp4" />
|
||||||
@@ -14,9 +7,9 @@
|
|||||||
</video>
|
</video>
|
||||||
</v-row>
|
</v-row>
|
||||||
<!-- Introduction section -->
|
<!-- Introduction section -->
|
||||||
<v-row v-if="introduction" class="d-flex justify-center align-center text-center mx-4">
|
<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>
|
<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>
|
<p class="text-xs text-black mb-8 mr-2 lg:mr-8 text-justify">{{ introduction.description }}</p>
|
||||||
</v-row>
|
</v-row>
|
||||||
<!-- Articles Section -->
|
<!-- Articles Section -->
|
||||||
<v-row>
|
<v-row>
|
||||||
@@ -106,26 +99,26 @@
|
|||||||
<p class="text-xs text-black mb-8 px-1 text-center">{{ summary.description }}</p>
|
<p class="text-xs text-black mb-8 px-1 text-center">{{ summary.description }}</p>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row class="justify-center">
|
<v-row class="justify-center">
|
||||||
<v-col cols="12" md="6" lg="8">
|
<v-col cols="12" md="6" lg="8"> <!-- Make it take more space on large screens -->
|
||||||
<v-expansion-panels variant="accordion">
|
<v-expansion-panels variant="accordion">
|
||||||
<v-expansion-panel v-for="(article, index) in articles" :key="article.id" class="article">
|
<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=""
|
<v-expansion-panel-title class="d-flex flex-column align-start" expand-icon="" collapse-icon="">
|
||||||
collapse-icon="">
|
<div class="d-flex align-center mb-2">
|
||||||
<div class="flex items-center mb-0">
|
<span class="mr-2">
|
||||||
<!-- Like/Dislike Section -->
|
<v-icon class="m-1 text-primary hover:text-green-500 text-xs"
|
||||||
<div class="mr-3 flex items-center">
|
|
||||||
<v-icon class="mr-1 text-xs" size="18" color="secondary"
|
|
||||||
@click.stop="likeArticle(article.id)">
|
@click.stop="likeArticle(article.id)">
|
||||||
mdi-thumb-up
|
mdi-thumb-up
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<p class="text-gray-400 text-sm">{{ article.likes }}</p>
|
<p class="ml-2 lg:ml-0 text-primary">{{ article.likes }}</p>
|
||||||
|
</span>
|
||||||
|
|
||||||
<v-icon class="mr-1 ml-2 text-xs" size="18" color="tertiary"
|
<span class="mr-3">
|
||||||
|
<v-icon class="m-1 text-tertiary hover:text-red-500 text-xs"
|
||||||
@click.stop="dislikeArticle(article.id)">
|
@click.stop="dislikeArticle(article.id)">
|
||||||
mdi-thumb-down
|
mdi-thumb-down
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<p class="text-gray-400 text-sm">{{ article.dislikes }}</p>
|
<p class="ml-2 lg:ml-0 text-tertiary">{{ article.dislikes }}</p>
|
||||||
</div>
|
</span>
|
||||||
<h2 class="text-sm text-wrap text-black font-bold m-0">
|
<h2 class="text-sm text-wrap text-black font-bold m-0">
|
||||||
{{ article.title }}
|
{{ article.title }}
|
||||||
</h2>
|
</h2>
|
||||||
@@ -139,107 +132,66 @@
|
|||||||
</div>
|
</div>
|
||||||
</v-expansion-panel-title>
|
</v-expansion-panel-title>
|
||||||
<v-expansion-panel-text>
|
<v-expansion-panel-text>
|
||||||
|
<v-expand-transition>
|
||||||
<v-timeline align="start" density="compact" class="ml-3">
|
<v-timeline align="start" density="compact" class="ml-3">
|
||||||
<v-timeline-item v-if="article.comments.length"
|
<v-timeline-item v-if="article.comments.length"
|
||||||
v-for="(comment, cIndex) in article.comments" :key="cIndex"
|
v-for="(comment, cIndex) in article.comments" :key="cIndex" dot-color="primary"
|
||||||
dot-color="secondary" size="x-small">
|
size="x-small">
|
||||||
<div class="flex items-center mb-0">
|
<div class="mb-0">
|
||||||
<!-- Like/Dislike Section -->
|
<p class="text-sm text-black">{{ comment }}</p>
|
||||||
<div class="mr-3 flex items-center">
|
|
||||||
<v-icon color="secondary" size="14"
|
|
||||||
@click="likeComment(article.id, comment.id)">mdi-thumb-up</v-icon>
|
|
||||||
<span class="text-xs text-gray-400 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-gray-400 ml-1">{{ comment.dislikes }}</span>
|
|
||||||
</div>
|
|
||||||
<!-- Comment Content -->
|
|
||||||
<p class="text-sm text-black">{{ comment.content }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</v-timeline-item>
|
</v-timeline-item>
|
||||||
|
|
||||||
<!-- No Comments Placeholder -->
|
<v-timeline-item v-else dot-color="secondary" size="x-small">
|
||||||
<v-timeline-item v-else dot-color="accent" size="x-small">
|
|
||||||
<p class="text-sm text-black">Aucune suggestion pour cet article</p>
|
<p class="text-sm text-black">Aucune suggestion pour cet article</p>
|
||||||
</v-timeline-item>
|
</v-timeline-item>
|
||||||
</v-timeline>
|
</v-timeline>
|
||||||
|
</v-expand-transition>
|
||||||
</v-expansion-panel-text>
|
</v-expansion-panel-text>
|
||||||
|
|
||||||
</v-expansion-panel>
|
</v-expansion-panel>
|
||||||
</v-expansion-panels>
|
</v-expansion-panels>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- Signature Section -->
|
<!-- Signature Section -->
|
||||||
<v-row class="justify-center w-full">
|
<v-row class="justify-center my-8">
|
||||||
<v-col cols="12" md="6" lg="8">
|
<v-col cols="12" md="8" lg="6">
|
||||||
<v-card elevation="3" class="pa-6 bg-gray-100" color="accent">
|
<v-card elevation="3" class="pa-6 bg-gray-100" color="secondary">
|
||||||
<v-card-title class="text-center">
|
<v-card-title class="text-center">
|
||||||
<h2 class="text-uppercase text-md font-semibold font-sans">Signer la Charte</h2>
|
<h2 class="text-uppercase text-md font-semibold font-sans">Signer la Charte</h2>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-form @submit.prevent="submitSignature" lazy-validation class="text-xs font-sans">
|
<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
|
<v-text-field v-model="name" label="Nom complet" outlined dense hide-details
|
||||||
hide-details :rules="[rules.required]" class="mb-2"
|
:rules="[rules.required]" class="mb-2" prepend-inner-icon="mdi-account"></v-text-field>
|
||||||
prepend-inner-icon="mdi-account"></v-text-field>
|
<v-text-field v-model="email" type="email" label="Email" placeholder="Entrez votre email"
|
||||||
<v-text-field v-model="email" type="email" label="Email"
|
outlined dense hide-details :rules="[rules.required, rules.email]" class="mb-2"
|
||||||
placeholder="Entrez votre email" outlined dense hide-details
|
|
||||||
:rules="[rules.required, rules.email]" class="mb-2"
|
|
||||||
prepend-inner-icon="mdi-email"></v-text-field>
|
prepend-inner-icon="mdi-email"></v-text-field>
|
||||||
<v-textarea v-model="comment" label="Ajouter un commentaire"
|
<v-textarea v-model="comment" label="Ajouter un commentaire"
|
||||||
placeholder="Partagez vos réflexions ou suggestions" outlined dense hide-details
|
placeholder="Partagez vos réflexions ou suggestions" outlined dense hide-details
|
||||||
rows="2" class="mb-2" prepend-inner-icon="mdi-message-text"></v-textarea>
|
rows="2" class="mb-2" prepend-inner-icon="mdi-message-text"></v-textarea>
|
||||||
<VBtnValid type="submit" block>
|
<VBtnValid :disabled="!isFormValid" type="submit" block>
|
||||||
<v-icon size="16" left>mdi-check-circle</v-icon>
|
<v-icon size="16" left>mdi-check-circle</v-icon>
|
||||||
Signer
|
Signer
|
||||||
</VBtnValid>
|
</VBtnValid>
|
||||||
</v-form>
|
</v-form>
|
||||||
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- Signature Display Section -->
|
|
||||||
<v-row class="justify-center">
|
|
||||||
<v-col cols="12" md="6" lg="8">
|
|
||||||
<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="accent" 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) -->
|
<!-- Success Overlay (Dynamic Content) -->
|
||||||
<v-overlay v-model="overlay.visible" class="d-flex align-center justify-center"
|
<v-overlay v-model="overlay" class="d-flex align-center justify-center"
|
||||||
:style="{ background: 'rgba(0, 0, 0, 0.5)', backdropFilter: 'blur(4px)' }">
|
: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-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>
|
<v-icon :color="overlayIconColor" size="48" class="mb-4">{{ overlayIcon }}</v-icon>
|
||||||
<p class="overlay-msg text-sm font-semibold text-black text-uppercase">{{ overlay.message }}</p>
|
<p class="overlay-msg text-sm font-semibold text-black text-uppercase">{{ overlayMessage }}</p>
|
||||||
<VBtnValid class="mt-6" @click="overlay.visible = false">
|
<VBtnValid class="mt-6" @click="overlay = false">
|
||||||
{{ overlay.buttonText }}
|
{{ overlayButtonText }}
|
||||||
</VBtnValid>
|
</VBtnValid>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
</v-row>
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -247,11 +199,6 @@
|
|||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useAsyncData } from 'nuxt/app';
|
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({
|
definePageMeta({
|
||||||
layout: 'default',
|
layout: 'default',
|
||||||
});
|
});
|
||||||
@@ -263,6 +210,7 @@ const rightColumnArticles = ref([]);
|
|||||||
const showStates = ref({});
|
const showStates = ref({});
|
||||||
const introduction = ref(null);
|
const introduction = ref(null);
|
||||||
const summary = ref(null);
|
const summary = ref(null);
|
||||||
|
|
||||||
const signatures = ref([]);
|
const signatures = ref([]);
|
||||||
// Form fields
|
// Form fields
|
||||||
const name = ref('');
|
const name = ref('');
|
||||||
@@ -270,15 +218,12 @@ const email = ref('');
|
|||||||
const comment = ref('');
|
const comment = ref('');
|
||||||
|
|
||||||
const isFormValid = computed(() => name.value && email.value);
|
const isFormValid = computed(() => name.value && email.value);
|
||||||
const isLoading = ref(true);
|
|
||||||
|
|
||||||
const overlay = ref({
|
const overlay = ref(false);
|
||||||
icon: '',
|
const overlayIcon = ref('mdi-check-circle'); // Default icon
|
||||||
message: '',
|
const overlayMessage = ref('Votre suggestion à été ajoutée avec succès'); // Default message
|
||||||
buttonText: '',
|
const overlayButtonText = ref('Continuer'); // Default button text
|
||||||
iconColor: '',
|
const overlayIconColor = ref('success');
|
||||||
visible: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
required: (value) => !!value || "Ce champ est obligatoire.",
|
required: (value) => !!value || "Ce champ est obligatoire.",
|
||||||
@@ -290,9 +235,7 @@ const toggleShow = (articleId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Fetch articles data
|
// Fetch articles data
|
||||||
const fetchContent = async () => {
|
const { data: contentData, error } = await useAsyncData("content", () =>
|
||||||
try {
|
|
||||||
const { data: contentData, error } = await useAsyncData('content', () =>
|
|
||||||
queryContent().find()
|
queryContent().find()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -302,6 +245,8 @@ const fetchContent = async () => {
|
|||||||
if (file.type === "intro") {
|
if (file.type === "intro") {
|
||||||
introduction.value = file;
|
introduction.value = file;
|
||||||
} else if (file.type === "article") {
|
} else if (file.type === "article") {
|
||||||
|
try {
|
||||||
|
// Add default values (likes, dislikes, comments) if missing
|
||||||
const article = {
|
const article = {
|
||||||
id: file.id,
|
id: file.id,
|
||||||
title: file.title,
|
title: file.title,
|
||||||
@@ -321,128 +266,118 @@ const fetchContent = async () => {
|
|||||||
title: article.title,
|
title: article.title,
|
||||||
description: article.description,
|
description: article.description,
|
||||||
likes: article.likes,
|
likes: article.likes,
|
||||||
dislikes: article.dislikes
|
dislikes: article.dislikes,
|
||||||
|
comments: article.comments,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error creating article:', err);
|
||||||
|
}
|
||||||
} else if (file.type === "summary") {
|
} else if (file.type === "summary") {
|
||||||
summary.value = file;
|
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);
|
|
||||||
// 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);
|
|
||||||
// Initialize newComments object based on article IDs
|
// Initialize newComments object based on article IDs
|
||||||
articles.value.forEach(article => {
|
articles.value.forEach(article => {
|
||||||
newComments.value[article.id] = '';
|
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 () => {
|
onMounted(async () => {
|
||||||
await fetchContent()
|
try {
|
||||||
await fetchArticlesData(articles);
|
const response = await $fetch(`/api/articles`, {
|
||||||
await fetchCommentsData(articles);
|
method: 'GET',
|
||||||
await fetchSignatures(signatures);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const likeArticle = (articleId) => {
|
if (response.success && response.data) {
|
||||||
updateLikeDislike({
|
// Update the articles array by merging data from the response
|
||||||
type: 'article',
|
articles.value = articles.value.map((article) => {
|
||||||
action: 'like',
|
const updatedArticle = response.data.find((data) => data.id === article.id);
|
||||||
id: articleId,
|
return updatedArticle
|
||||||
articles,
|
? {
|
||||||
|
...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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const dislikeArticle = (articleId) => {
|
|
||||||
updateLikeDislike({
|
|
||||||
type: 'article',
|
|
||||||
action: 'dislike',
|
|
||||||
id: articleId,
|
|
||||||
articles,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const likeComment = (articleId, commentId) => {
|
const likeArticle = async (articleId) => {
|
||||||
updateLikeDislike({
|
const article = articles.value.find((a) => a.id === articleId);
|
||||||
type: 'comment',
|
if (!article) return;
|
||||||
action: 'like',
|
article.likes += 1;
|
||||||
id: commentId,
|
|
||||||
articleId,
|
|
||||||
articles,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const dislikeComment = (articleId, commentId) => {
|
try {
|
||||||
updateLikeDislike({
|
await $fetch(`/api/articles/${articleId}`, {
|
||||||
type: 'comment',
|
method: 'PUT',
|
||||||
action: 'dislike',
|
body: { likes: article.likes },
|
||||||
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 addComment = async (articleId) => {
|
const addComment = async (articleId) => {
|
||||||
const commentText = newComments.value[articleId]?.trim();
|
const commentText = newComments.value[articleId]?.trim();
|
||||||
if (!commentText) return;
|
if (!commentText) return;
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await $fetch('/api/comments', {
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
content: commentText,
|
|
||||||
articleId,
|
|
||||||
likes: 0,
|
|
||||||
dislikes: 0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
const article = articles.value.find((a) => a.id === articleId);
|
const article = articles.value.find((a) => a.id === articleId);
|
||||||
if (article) {
|
article.comments.push(commentText);
|
||||||
article.comments.push({
|
|
||||||
id: response.data.id,
|
|
||||||
content: commentText,
|
|
||||||
likes: 0,
|
|
||||||
dislikes: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the input field for the current article
|
|
||||||
newComments.value[articleId] = '';
|
newComments.value[articleId] = '';
|
||||||
|
|
||||||
// Display success overlay
|
overlayIcon.value = 'mdi-check-circle';
|
||||||
showOverlay(
|
overlayMessage.value = `Merci pour votre suggestion. Veuillez la consulter tout en descendant vers le bas de la page`;
|
||||||
{
|
overlayButtonText.value = 'Continuer';
|
||||||
icon: 'mdi-check-circle',
|
overlayIconColor.value = 'success';
|
||||||
message: `Merci pour votre suggestion. Veuillez la consulter tout en descendant vers le bas de la page.`,
|
overlay.value = true;
|
||||||
buttonText: 'Continuer',
|
try {
|
||||||
iconColor: 'success',
|
await $fetch(`/api/articles/${article.id}`, {
|
||||||
},
|
method: 'PUT',
|
||||||
overlay
|
body: { comments: article.comments },
|
||||||
);
|
});
|
||||||
|
|
||||||
console.log('Comment added successfully');
|
console.log('Comment added successfully');
|
||||||
} else {
|
|
||||||
console.error('Failed to add comment:', response.message);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in addComment:', error);
|
console.error('Error in addComment:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitSignature = async () => {
|
const submitSignature = async () => {
|
||||||
if (isFormValid.value) {
|
|
||||||
const signatureData = {
|
const signatureData = {
|
||||||
name: name.value,
|
name: name.value,
|
||||||
email: email.value,
|
email: email.value,
|
||||||
@@ -457,27 +392,21 @@ const submitSignature = async () => {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(signatureData)
|
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');
|
console.log('Charte is submitted successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in submit signature:', error);
|
console.error('Error in submit signature:', error);
|
||||||
}
|
}
|
||||||
} return;
|
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 = '';
|
||||||
};
|
};
|
||||||
</script>
|
|
||||||
|
|
||||||
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.video-section {
|
.video-section {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -522,8 +451,4 @@ p {
|
|||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-icon:hover {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
38
plugins/vuetify.ts
Normal file
38
plugins/vuetify.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 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)
|
||||||
|
})
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
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;
|
|
||||||
@@ -13,18 +13,7 @@ model Article {
|
|||||||
description String
|
description String
|
||||||
likes Int @default(0)
|
likes Int @default(0)
|
||||||
dislikes Int @default(0)
|
dislikes Int @default(0)
|
||||||
comments Comment[]
|
comments String[] @default([])
|
||||||
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())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|||||||
1
public/robots.txt
Normal file
1
public/robots.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -2,18 +2,14 @@ import prisma from '~/lib/prisma';
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const id = event.context.params?.id;
|
const id = event.context.params?.id;
|
||||||
|
console.log('prisma' + event.context.params?.id)
|
||||||
try {
|
try {
|
||||||
const article = await prisma.article.findUnique({
|
const article = await prisma.article.findUnique({ where: { id } });
|
||||||
where: { id },
|
|
||||||
include: { comments: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!article) {
|
if (!article) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
data: null,
|
data: null,
|
||||||
message: 'Article not found.',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,19 +15,19 @@ export default defineEventHandler(async (event) => {
|
|||||||
const updatedArticle = await prisma.article.update({
|
const updatedArticle = await prisma.article.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
|
id: body.id,
|
||||||
title: body.title,
|
title: body.title,
|
||||||
description: body.description,
|
description: body.description,
|
||||||
likes: body.likes,
|
likes: body.likes,
|
||||||
dislikes: body.dislikes,
|
dislikes: body.dislikes,
|
||||||
|
comments: body.comments,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: updatedArticle,
|
data: updatedArticle,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating article:', error);
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import prisma from '~/lib/prisma';
|
|||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const articles = await prisma.article.findMany({
|
const articles = await prisma.article.findMany();
|
||||||
include: { comments: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!articles || articles.length === 0) {
|
if (!articles || articles.length === 0) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ import prisma from '~/lib/prisma';
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
const { title, description, likes, dislikes } = body;
|
const { id, title, description, likes, dislikes, comments } = body;
|
||||||
|
|
||||||
const newArticle = await prisma.article.create({
|
const newArticle = await prisma.article.create({
|
||||||
data: {
|
data: {
|
||||||
|
id,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
likes: likes || 0,
|
likes: likes || 0,
|
||||||
dislikes: dislikes || 0,
|
dislikes: dislikes || 0,
|
||||||
|
comments: comments || [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user