Merge branch 'notifications'
This commit is contained in:
commit
caf6f721fb
34 changed files with 671 additions and 417 deletions
|
|
@ -117,7 +117,7 @@ const { page } = storeToRefs(usePageStore());
|
|||
const unreadNotificationsCount = computed(() => {
|
||||
if (!user.value) return undefined;
|
||||
const count = user.value.notifications.filter(
|
||||
(notification) => notification.isRead
|
||||
(notification) => notification.isread != "true"
|
||||
).length;
|
||||
if (count === 0) return undefined;
|
||||
return count;
|
||||
|
|
@ -164,7 +164,7 @@ function hasUnreadNotification(project) {
|
|||
if (!user.value) return false;
|
||||
return user.value.notifications.some((notification) => {
|
||||
return (
|
||||
notification.isRead != true &&
|
||||
notification.isread != "true" &&
|
||||
project.uri.includes(notification.location.project.uri)
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<article
|
||||
:id="`comment-${comment.id}`"
|
||||
class="comment | flow"
|
||||
:data-status="status"
|
||||
:data-status="getStatus"
|
||||
@click="read()"
|
||||
>
|
||||
<header>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<time
|
||||
class="comment__date"
|
||||
:datetime="dayjs(comment.date).format('YYYY-MM-DD')"
|
||||
>{{ formatDate() }}</time
|
||||
>{{ formatDate(comment.date) }}</time
|
||||
>
|
||||
</p>
|
||||
</header>
|
||||
|
|
@ -70,11 +70,11 @@ const api = useApiStore();
|
|||
const dialog = useDialogStore();
|
||||
|
||||
// Functions
|
||||
const status = computed(() => {
|
||||
const getStatus = computed(() => {
|
||||
const correspondingNotification = userStore.notifications.find(
|
||||
(notification) => notification.id === comment.id
|
||||
);
|
||||
if (correspondingNotification && !correspondingNotification.isRead) {
|
||||
if (correspondingNotification && correspondingNotification.isread != "true") {
|
||||
return "unread";
|
||||
}
|
||||
return undefined;
|
||||
|
|
@ -101,9 +101,10 @@ function closeAddField() {
|
|||
}
|
||||
|
||||
async function read() {
|
||||
if (status.value !== "unread") return;
|
||||
if (getStatus.value !== "unread") return;
|
||||
try {
|
||||
const newNotification = await api.readNotification(comment.id);
|
||||
console.log(newNotification);
|
||||
userStore.readNotification(comment.id);
|
||||
} catch (error) {
|
||||
console.log("Erreur lors de la lecture de la notification : ", error);
|
||||
|
|
@ -112,9 +113,10 @@ async function read() {
|
|||
|
||||
async function deleteComment(event) {
|
||||
event.stopPropagation();
|
||||
console.log(comment);
|
||||
const newFile = await api.deleteComment(comment);
|
||||
dialog.updateFile(newFile);
|
||||
if (comment.parentId) {
|
||||
if (comment.type === "comment-reply") {
|
||||
emits("close:comment");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ function handleSubmit(event = null) {
|
|||
}
|
||||
const date = dayjs().format();
|
||||
const newComment = {
|
||||
path: route.fullPath,
|
||||
dialogUri: route.fullPath,
|
||||
fileName: openedFile ? openedFile.value.name : false,
|
||||
userUuid: user.uuid,
|
||||
text: draftComment.value.text,
|
||||
|
|
|
|||
46
src/components/notifications/Comment.vue
Normal file
46
src/components/notifications/Comment.vue
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<article
|
||||
class="notification | bg-white rounded-lg | p-16 | flow"
|
||||
:data-status="notification.isread == 'true' ? 'read' : 'unread'"
|
||||
data-type="comment"
|
||||
@click="router.push(notification.location.dialoguri + '&comments=true')"
|
||||
>
|
||||
<header>
|
||||
<p class="flex">
|
||||
<strong
|
||||
class="notification__type | font-medium text-primary"
|
||||
data-icon="comment"
|
||||
>Commentaire</strong
|
||||
>
|
||||
<span class="notification__client | text-grey-700">{{
|
||||
notification.location.project.title
|
||||
}}</span>
|
||||
<time
|
||||
datetime=""
|
||||
class="notification__date | text-grey-700 | ml-auto"
|
||||
>{{ formatDate(notification) }}</time
|
||||
>
|
||||
</p>
|
||||
</header>
|
||||
<p
|
||||
v-if="notification.type"
|
||||
class="notification__body | text-md font-medium | line-clamp"
|
||||
>
|
||||
{{
|
||||
notification.author.name
|
||||
? notification.author.name
|
||||
: notification.author.email
|
||||
}}
|
||||
: {{ notification.text }}
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { useNotificationsStore } from "../../stores/notifications";
|
||||
|
||||
const router = useRouter();
|
||||
const { notification } = defineProps({ notification: Object });
|
||||
const { formatDate } = useNotificationsStore();
|
||||
</script>
|
||||
45
src/components/notifications/Content.vue
Normal file
45
src/components/notifications/Content.vue
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<article
|
||||
class="notification | bg-white rounded-lg | p-16 | flow"
|
||||
:data-status="notification.isread == 'true' ? 'read' : 'unread'"
|
||||
data-type="content"
|
||||
@click="
|
||||
read(notification);
|
||||
router.push(notification.location.page.uri);
|
||||
"
|
||||
title="Aller au contenu"
|
||||
>
|
||||
<header>
|
||||
<p class="flex">
|
||||
<strong
|
||||
class="notification__type | font-medium text-primary"
|
||||
data-icon="content"
|
||||
>Contenu</strong
|
||||
>
|
||||
<span class="notification__client | text-grey-700">{{
|
||||
notification.location.project.title
|
||||
}}</span>
|
||||
<time
|
||||
datetime=""
|
||||
class="notification__date | text-grey-700 | ml-auto"
|
||||
>{{ formatDate(notification) }}</time
|
||||
>
|
||||
</p>
|
||||
</header>
|
||||
<p
|
||||
v-if="notification.type"
|
||||
class="notification__body | text-md font-medium | line-clamp"
|
||||
>
|
||||
{{ notification.text }}
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { useNotificationsStore } from "../../stores/notifications";
|
||||
|
||||
const router = useRouter();
|
||||
const { notification } = defineProps({ notification: Object });
|
||||
const { formatDate, read } = useNotificationsStore();
|
||||
</script>
|
||||
51
src/components/notifications/Reply.vue
Normal file
51
src/components/notifications/Reply.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<article
|
||||
class="notification | bg-white rounded-lg | p-16 | flow"
|
||||
:data-status="notification.isread == 'true' ? 'read' : 'unread'"
|
||||
data-type="comment"
|
||||
@click="router.push(notification.location.dialoguri + '&comments=true')"
|
||||
>
|
||||
<header>
|
||||
<p class="flex">
|
||||
<strong
|
||||
class="notification__type | font-medium text-primary"
|
||||
data-icon="comment"
|
||||
>Réponse à
|
||||
{{
|
||||
notification.location.parent.author.name
|
||||
? notification.location.parent.author.name
|
||||
: notification.location.parent.author.email
|
||||
}}</strong
|
||||
>
|
||||
<span class="notification__client | text-grey-700">{{
|
||||
notification.location.project.title
|
||||
}}</span>
|
||||
<time
|
||||
datetime=""
|
||||
class="notification__date | text-grey-700 | ml-auto"
|
||||
>{{ formatDate(notification) }}</time
|
||||
>
|
||||
</p>
|
||||
</header>
|
||||
<p
|
||||
v-if="notification.type"
|
||||
class="notification__body | text-md font-medium | line-clamp"
|
||||
>
|
||||
{{
|
||||
notification.author.name
|
||||
? notification.author.name
|
||||
: notification.author.email
|
||||
}}
|
||||
: {{ notification.text }}
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { useNotificationsStore } from "../../stores/notifications";
|
||||
|
||||
const router = useRouter();
|
||||
const { notification } = defineProps({ notification: Object });
|
||||
const { formatDate } = useNotificationsStore();
|
||||
</script>
|
||||
|
|
@ -36,6 +36,11 @@ const draftComment = ref(null);
|
|||
|
||||
const isViewerDisabled = ref(false);
|
||||
|
||||
watch(openedFile, () => {
|
||||
removeCommentMarkers();
|
||||
setCommentMarkers();
|
||||
});
|
||||
|
||||
watch(isCommentsOpen, (newVal) => {
|
||||
if (newVal) {
|
||||
setCommentMarkers();
|
||||
|
|
@ -45,6 +50,8 @@ watch(isCommentsOpen, (newVal) => {
|
|||
});
|
||||
|
||||
watch(openedFile, (newVal) => {
|
||||
if (!location.href.includes("virtual-sample")) return;
|
||||
|
||||
isViewerDisabled.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -223,7 +223,6 @@ function getFrontView(track) {
|
|||
const extension = track.files[0].name.split(".")[1];
|
||||
const frontViewName = "0_" + xFrontView + "." + extension;
|
||||
const frontView = track.files.find((file) => file.name === frontViewName);
|
||||
console.log(frontView);
|
||||
return frontView;
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
45
src/stores/notifications.js
Normal file
45
src/stores/notifications.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { defineStore } from "pinia";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/fr";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useApiStore } from "./api";
|
||||
|
||||
export const useNotificationsStore = defineStore("notifications", () => {
|
||||
dayjs.locale("fr");
|
||||
|
||||
const router = useRouter();
|
||||
const api = useApiStore();
|
||||
|
||||
function formatDate(notification) {
|
||||
const notificationDigitalDate = parseInt(
|
||||
dayjs(notification.date).format("YYYYMMDD")
|
||||
);
|
||||
const todayDigitalDate = parseInt(dayjs().format("YYYYMMDD"));
|
||||
const isToday = notificationDigitalDate === todayDigitalDate;
|
||||
|
||||
if (isToday) {
|
||||
return dayjs(notification.date).format("HH:mm");
|
||||
} else if (todayDigitalDate === notificationDigitalDate + 1) {
|
||||
return "hier";
|
||||
} else {
|
||||
return dayjs(notification.date).format("DD MMM YY");
|
||||
}
|
||||
}
|
||||
|
||||
function read(notification) {
|
||||
if (!notification.id) {
|
||||
console.error(
|
||||
"Couldn't change notification status because it has no id."
|
||||
);
|
||||
} else if (notification.isread != true) {
|
||||
api
|
||||
.readNotification(notification.id)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
.catch((err) => console.error("Notification could not be read.", err));
|
||||
}
|
||||
}
|
||||
|
||||
return { formatDate, read };
|
||||
});
|
||||
|
|
@ -13,14 +13,14 @@ export const useUserStore = defineStore("user", () => {
|
|||
function readNotification(notificationId) {
|
||||
user.value.notifications.forEach((notification) => {
|
||||
if (notification.id === notificationId) {
|
||||
notification.isRead = true;
|
||||
notification.isread = "true";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function readAllNotifications(notificationId) {
|
||||
user.value.notifications.forEach((notification) => {
|
||||
notification.isRead = true;
|
||||
notification.isread = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ function toPascalCase(string) {
|
|||
});
|
||||
}
|
||||
|
||||
const StringUtils = { toPascalCase };
|
||||
function urlToPath(url) {
|
||||
return url.replace(window.location.origin, "");
|
||||
}
|
||||
|
||||
const StringUtils = { toPascalCase, urlToPath };
|
||||
|
||||
export default StringUtils;
|
||||
|
|
|
|||
|
|
@ -38,64 +38,11 @@
|
|||
v-for="notification in sortedNotifications"
|
||||
:key="notification.id"
|
||||
>
|
||||
<article
|
||||
class="notification | bg-white rounded-lg | p-16 | flow"
|
||||
:data-status="notification.isRead ? 'read' : 'unread'"
|
||||
:data-type="notification.type"
|
||||
@click="read(notification)"
|
||||
>
|
||||
<header>
|
||||
<p class="flex">
|
||||
<strong
|
||||
class="notification__type | font-medium text-primary"
|
||||
:data-icon="notification.type"
|
||||
>Nouveau
|
||||
{{
|
||||
notification.type === "comment" ? "commentaire" : "contenu"
|
||||
}}</strong
|
||||
>
|
||||
<span class="notification__client | text-grey-700">{{
|
||||
notification.location.project.title
|
||||
}}</span>
|
||||
<time
|
||||
datetime=""
|
||||
class="notification__date | text-grey-700 | ml-auto"
|
||||
>{{ formatDate(notification) }}</time
|
||||
>
|
||||
</p>
|
||||
</header>
|
||||
<p
|
||||
v-if="notification.type"
|
||||
class="notification__body | text-md font-medium | line-clamp"
|
||||
>
|
||||
{{ notification.text }}
|
||||
</p>
|
||||
</article>
|
||||
<component
|
||||
:is="notificationComponents[notification.type]"
|
||||
:notification="notification"
|
||||
/>
|
||||
</template>
|
||||
<!-- <article
|
||||
class="notification | bg-white rounded-lg | p-16 | flow"
|
||||
data-status="read"
|
||||
data-type="content"
|
||||
>
|
||||
<header>
|
||||
<p class="flex">
|
||||
<strong
|
||||
class="notification__type | font-medium text-primary"
|
||||
data-icon="content"
|
||||
>Nouveau contenu</strong
|
||||
>
|
||||
<span class="notification__client | text-grey-700"></span>
|
||||
<time
|
||||
datetime=""
|
||||
class="notification__date | text-grey-700 | ml-auto"
|
||||
>Hier</time
|
||||
>
|
||||
</p>
|
||||
</header>
|
||||
<p class="notification__body | text-md font-medium | line-clamp">
|
||||
Proposition 2
|
||||
</p>
|
||||
</article> -->
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
|
@ -108,7 +55,9 @@ import { useUserStore } from "../stores/user";
|
|||
import { ref, computed } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useApiStore } from "../stores/api";
|
||||
import { useRouter } from "vue-router";
|
||||
import Comment from "../components/notifications/Comment.vue";
|
||||
import Reply from "../components/notifications/Reply.vue";
|
||||
import Content from "../components/notifications/Content.vue";
|
||||
|
||||
dayjs.locale("fr");
|
||||
|
||||
|
|
@ -135,7 +84,11 @@ const tabs = computed(() => {
|
|||
];
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const notificationComponents = {
|
||||
comment: Comment,
|
||||
"comment-reply": Reply,
|
||||
content: Content,
|
||||
};
|
||||
|
||||
const sortedNotifications = computed(() => {
|
||||
return [...notifications.value].sort((a, b) => {
|
||||
|
|
@ -147,22 +100,6 @@ function changeTab(newValue) {
|
|||
currentTab.value = newValue;
|
||||
}
|
||||
|
||||
function formatDate(notification) {
|
||||
const notificationDigitalDate = parseInt(
|
||||
dayjs(notification.date).format("YYYYMMDD")
|
||||
);
|
||||
const todayDigitalDate = parseInt(dayjs().format("YYYYMMDD"));
|
||||
const isToday = notificationDigitalDate === todayDigitalDate;
|
||||
|
||||
if (isToday) {
|
||||
return dayjs(notification.date).format("HH:mm");
|
||||
} else if (todayDigitalDate === notificationDigitalDate + 1) {
|
||||
return "hier";
|
||||
} else {
|
||||
return dayjs(notification.date).format("DD MMM YY");
|
||||
}
|
||||
}
|
||||
|
||||
function readAll() {
|
||||
try {
|
||||
api.readAllNotifications();
|
||||
|
|
@ -171,24 +108,6 @@ function readAll() {
|
|||
console.log("Could not read all notifications : ", error);
|
||||
}
|
||||
}
|
||||
|
||||
function read(notification) {
|
||||
if (!notification.id) {
|
||||
console.error("Couldn't change notification status because it has no id.");
|
||||
} else if (!notification.isRead) {
|
||||
api
|
||||
.readNotification(notification.id)
|
||||
.then((res) => {
|
||||
router.push(toPath(notification.location.href));
|
||||
})
|
||||
.catch((err) => console.error("Notification could not be read.", err));
|
||||
}
|
||||
router.push(toPath(notification.location.href));
|
||||
}
|
||||
|
||||
function toPath(string) {
|
||||
return string.replace(window.location.origin, "");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
@ -207,13 +126,17 @@ main > header [role="tablist"] {
|
|||
.notifications {
|
||||
--flow-space: var(--space-16);
|
||||
}
|
||||
.notification {
|
||||
</style>
|
||||
|
||||
<!-- WARNING : below styles are not scoped so as to be applied to the children components -->
|
||||
<style>
|
||||
.notifications .notification {
|
||||
--flow-space: var(--space-12);
|
||||
font-size: var(--text-sm);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.notification[data-status="unread"]::after {
|
||||
.notifications .notification[data-status="unread"]::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 0.5rem;
|
||||
|
|
@ -225,22 +148,22 @@ main > header [role="tablist"] {
|
|||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
.notification p {
|
||||
.notifications .notification p {
|
||||
max-width: 100%;
|
||||
}
|
||||
.notification header p {
|
||||
.notifications .notification header p {
|
||||
column-gap: 0;
|
||||
}
|
||||
.notification__type {
|
||||
.notifications .notification__type {
|
||||
display: flex;
|
||||
}
|
||||
.notification__type::before {
|
||||
.notifications .notification__type::before {
|
||||
margin-right: var(--space-8);
|
||||
}
|
||||
.notification__client {
|
||||
.notifications .notification__client {
|
||||
display: inline-block;
|
||||
}
|
||||
.notification__client::before {
|
||||
.notifications .notification__client::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 3px;
|
||||
|
|
@ -249,7 +172,7 @@ main > header [role="tablist"] {
|
|||
border-radius: 50%;
|
||||
margin: 0.2em 0.5em;
|
||||
}
|
||||
.notification__body {
|
||||
.notifications .notification__body {
|
||||
--line-clamp: 2;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue