read comment notification working

This commit is contained in:
isUnknown 2025-01-10 17:40:45 +01:00
parent 9222069ef5
commit 246d21f85a
17 changed files with 86 additions and 111 deletions

View file

@ -40,6 +40,5 @@ fields:
disabled: true disabled: true
date: date:
type: hidden type: hidden
isread: readby:
type: toggle type: users
disabled: true

View file

@ -1,6 +1,3 @@
title: Admin title: Admin
description: Possède tous les droits et les accès, peut accéder à lensemble des Clients et des Projets et assigner des Projets à des Utilisateurs. description: Possède tous les droits et les accès, peut accéder à lensemble des Clients et des Projets et assigner des Projets à des Utilisateurs.
home: /panel/pages/projects home: /panel/pages/projects
fields:
notifications: fields/notifications

View file

@ -5,12 +5,7 @@ permissions:
panel: false panel: false
fields: fields:
client:
type: pages
max: 1
query: page('clients').children
projects: projects:
label: Projets label: Projets
type: pages type: pages
query: page('projects').children query: page('projects').children
notifications: fields/notifications

View file

@ -19,4 +19,3 @@ fields:
type: pages type: pages
query: page('projects').children query: page('projects').children
width: 3/4 width: 3/4
notifications: fields/notifications

View file

@ -10,11 +10,10 @@ return function ($page, $kirby, $site) {
$userData = [ $userData = [
"role" => (string) $kirby->user()->role(), "role" => (string) $kirby->user()->role(),
"uuid" => (string) $kirby->user()->uuid(), "uuid" => (string) $kirby->user()->uuid()
"notifications" => Yaml::decode($kirby->user()->notifications()->value()),
]; ];
if ($kirby->user()->role() == 'client' && $kirby->user()->client()->isNotEmpty()) { if ($kirby->user()->client()->exists() && $kirby->user()->client()->isNotEmpty()) {
$userData['client'] = [ $userData['client'] = [
"name" => (string) $kirby->user()->client()->toPage()->title(), "name" => (string) $kirby->user()->client()->toPage()->title(),
"uuid" => (string) $kirby->user()->client()->toPage()->uuid() "uuid" => (string) $kirby->user()->client()->toPage()->uuid()

View file

@ -52,7 +52,7 @@ return [
try { try {
$project->createNotification($commentData); $project->createNotification($commentData);
} catch (\Throwable $th) { } catch (\Throwable $th) {
throw new Exception($th->getMessage() . ". Line " . $th->getLine() . " in file " . $th->getFile(), 1); throw new Exception($th->getMessage() . ". line " . $th->getLine() . " in file " . $th->getFile(), 1);
} }
exit; exit;

View file

@ -7,7 +7,17 @@ return [
$json = file_get_contents('php://input'); $json = file_get_contents('php://input');
$data = json_decode($json); $data = json_decode($json);
$newNotifications = kirby()->user()->readNotification($data->notificationId); try {
return $newNotifications; $project = page($data->projectUuid);
$project->readNotification($data->notificationId);
return json_encode([
"status" => "success"
]);
} catch (\Throwable $th) {
return json_encode([
"status" => "error",
"message" => $th->getMessage() . ' line ' . $th->getLine() . " in file " . $th->getFile()
]);
}
} }
]; ];

View file

@ -12,7 +12,7 @@ class Notification
protected Author $author; protected Author $author;
protected string $date; protected string $date;
protected string $id; protected string $id;
protected string $isread = "false"; protected array $readby = [];
protected ?Position $position = null; protected ?Position $position = null;
@ -33,7 +33,7 @@ class Notification
"author" => $this->author->toArray(), "author" => $this->author->toArray(),
"date" => $this->date, "date" => $this->date,
"id" => $this->id, "id" => $this->id,
"isread" => $this->isread, "readby" => $this->readby,
]; ];
return $array; return $array;

View file

@ -31,7 +31,36 @@ class NotificationsPage extends Page {
} }
$this->update([ $this->update([
'notifications' => Yaml::encode(array_values($notifications)) 'notifications' => $notifications
]); ]);
} }
public function readNotification($notificationId) {
$notifications = $this->notifications()->isNotEmpty()
? Yaml::decode($this->notifications()->value())
: [];
foreach ($notifications as $key => &$notification) {
if ($notification['id'] !== $notificationId) {
continue;
}
if (!isset($notification["readby"])) {
$notification["readby"] = [];
}
$userUuid = (string) kirby()->user()->uuid();
if (in_array($userUuid, $notification["readby"])) {
return;
}
$notification["readby"][] = $userUuid;
break;
}
$this->update([
'notifications' => Yaml::encode($notifications)
]);
}
} }

View file

@ -115,15 +115,15 @@ import { useProjectStore } from "../stores/project";
const route = useRoute(); const route = useRoute();
const isExpanded = ref(true); const isExpanded = ref(true);
const { user } = storeToRefs(useUserStore()); const { user, notifications } = storeToRefs(useUserStore());
const { currentProjects, archivedProjects } = storeToRefs(useProjectsStore()); const { currentProjects, archivedProjects } = storeToRefs(useProjectsStore());
const { isEmptyBrief } = useProjectStore(); const { isEmptyBrief } = useProjectStore();
const { page } = storeToRefs(usePageStore()); const { page } = storeToRefs(usePageStore());
const unreadNotificationsCount = computed(() => { const unreadNotificationsCount = computed(() => {
if (!user.value) return undefined; if (!user.value) return undefined;
const count = user.value.notifications.filter( const count = notifications.value.filter(
(notification) => notification.isread != "true" (notification) => !notification.isRead
).length; ).length;
if (count === 0) return undefined; if (count === 0) return undefined;
return count; return count;
@ -168,7 +168,7 @@ function isCurrent(navItem) {
function hasUnreadNotification(project) { function hasUnreadNotification(project) {
if (!user.value) return false; if (!user.value) return false;
return user.value.notifications.some((notification) => { return notifications.value.some((notification) => {
return ( return (
notification.isread != "true" && notification.isread != "true" &&
project.uri.includes(notification.location.project.uri) project.uri.includes(notification.location.project.uri)

View file

@ -109,7 +109,7 @@ const getStatus = computed(() => {
const correspondingNotification = userStore.notifications.find( const correspondingNotification = userStore.notifications.find(
(notification) => notification.id === comment.id (notification) => notification.id === comment.id
); );
if (correspondingNotification && correspondingNotification.isread != "true") { if (correspondingNotification && !correspondingNotification.isRead) {
return "unread"; return "unread";
} }
return undefined; return undefined;
@ -143,7 +143,7 @@ function handleClick() {
async function read() { async function read() {
if (getStatus.value !== "unread") return; if (getStatus.value !== "unread") return;
try { try {
const newNotification = await api.readNotification(comment.id); const newNotification = await api.readNotification(comment);
console.log(newNotification); console.log(newNotification);
userStore.readNotification(comment.id); userStore.readNotification(comment.id);
} catch (error) { } catch (error) {

View file

@ -1,7 +1,6 @@
<template> <template>
<article <article
class="notification | bg-white rounded-lg | p-16 | flow" class="notification | bg-white rounded-lg | p-16 | flow"
:data-status="notification.isread == 'true' ? 'read' : 'unread'"
data-type="comment" data-type="comment"
@click="router.push(notification.location.dialoguri + '&comments=true')" @click="router.push(notification.location.dialoguri + '&comments=true')"
> >

View file

@ -1,7 +1,6 @@
<template> <template>
<article <article
class="notification | bg-white rounded-lg | p-16 | flow" class="notification | bg-white rounded-lg | p-16 | flow"
:data-status="notification.isread == 'true' ? 'read' : 'unread'"
data-type="content" data-type="content"
@click=" @click="
read(notification); read(notification);

View file

@ -1,7 +1,6 @@
<template> <template>
<article <article
class="notification | bg-white rounded-lg | p-16 | flow" class="notification | bg-white rounded-lg | p-16 | flow"
:data-status="notification.isread == 'true' ? 'read' : 'unread'"
data-type="comment" data-type="comment"
@click="router.push(notification.location.dialoguri + '&comments=true')" @click="router.push(notification.location.dialoguri + '&comments=true')"
> >

View file

@ -2,35 +2,6 @@ import { defineStore } from "pinia";
import uniqid from "uniqid"; import uniqid from "uniqid";
export const useApiStore = defineStore("api", () => { export const useApiStore = defineStore("api", () => {
/**
* Asynchronously fetches JSON data corresponding to a given path.
*
* @param {string} [path=window.location.pathname] - The path for which to fetch data.
* - If no path is provided, the function will use the current page's path.
* - If the path is "/", it is assumed to be the homepage, and "home.json" is fetched.
* - For other paths, the function will append ".json" to the path and fetch that URL.
*
* @returns {Promise<Object>} A promise that resolves to the JSON data if the fetch is successful.
*
* @throws {Error} Will throw an error if the HTTP request fails (e.g., non-200 status code).
* - The error will include the HTTP status code and a message indicating the fetch failed.
*
* @example
* // Fetch data for the current page
* fetchData().then(data => {
* console.log(data);
* }).catch(error => {
* console.error('Error fetching data:', error);
* });
*
* @example
* // Fetch data for a specific path
* fetchData('/about').then(data => {
* console.log(data);
* }).catch(error => {
* console.error('Error fetching data:', error);
* });
*/
async function fetchData(path = window.location.pathname) { async function fetchData(path = window.location.pathname) {
const isHomePage = path === "/"; const isHomePage = path === "/";
path = path === "/" ? "/home" : path; path = path === "/" ? "/home" : path;
@ -57,47 +28,6 @@ export const useApiStore = defineStore("api", () => {
} }
} }
function fetchDataThroughKQL() {
const api = "/api/query";
const username = import.meta.env.VITE_USERNAME;
const password = import.meta.env.VITE_PASSWORD;
const token = btoa(`${username}:${password}`);
const headers = {
Authorization: `Basic ${token}`,
};
const request = {
method: "post",
body: JSON.stringify({
query: `page('home')`,
select: {
testImages: {
query: "page.testImages.toFiles",
select: {
url: true,
},
},
blocks: {
query: "page.testBlocks.toBlocks",
},
},
}),
headers,
};
fetch(api, request)
.then((response) => response.json())
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
}
async function fetchRoute(path, method, data) { async function fetchRoute(path, method, data) {
const config = { const config = {
method: method, method: method,
@ -212,11 +142,12 @@ export const useApiStore = defineStore("api", () => {
} }
} }
async function readNotification(notificationId) { async function readNotification(comment) {
const headers = { const headers = {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
notificationId, projectUuid: comment.location.project.uuid,
notificationId: comment.id,
}), }),
}; };
try { try {
@ -278,7 +209,6 @@ export const useApiStore = defineStore("api", () => {
} }
return { return {
fetchDataThroughKQL,
fetchData, fetchData,
fetchRoute, fetchRoute,
addComment, addComment,

View file

@ -1,26 +1,45 @@
import { defineStore } from "pinia"; import { defineStore, storeToRefs } from "pinia";
import { ref, computed } from "vue"; import { ref, computed } from "vue";
import { useProjectsStore } from "./projects";
export const useUserStore = defineStore("user", () => { export const useUserStore = defineStore("user", () => {
const user = ref(null); const user = ref(null);
const { projects } = storeToRefs(useProjectsStore());
const notifications = computed(() => { const notifications = computed(() => {
return typeof user.value.notifications === "array" return projects.value.reduce((acc, project) => {
? user.value.notifications if (!project.notifications) return acc;
: Object.values(user.value.notifications);
const projectNotifications = project.notifications.map(
(notification) => ({
...notification,
project: {
id: project.id,
name: project.title,
},
isRead:
notification.author.uuid === user.value.uuid ||
notification.readby?.includes(user.value.uuid),
})
);
return [...acc, ...projectNotifications];
}, []);
}); });
function readNotification(notificationId) { function readNotification(notificationId) {
user.value.notifications.forEach((notification) => { notifications.value = notifications.value.map((notification) => {
if (notification.id === notificationId) { if (notification.id === notificationId) {
notification.isread = "true"; notification.isRead = true;
} }
return notification;
}); });
} }
function readAllNotifications(notificationId) { function readAllNotifications(notificationId) {
user.value.notifications.forEach((notification) => { user.value.notifications.forEach((notification) => {
notification.isread = true; notification.isRead = true;
}); });
} }

View file

@ -44,6 +44,7 @@
<component <component
:is="notificationComponents[notification.type]" :is="notificationComponents[notification.type]"
:notification="notification" :notification="notification"
:data-status="notification.isRead ? 'read' : 'unread'"
/> />
</template> </template>
</section> </section>