main
vue 232 lines 4.57 KB
Raw
1 <template>
2 <n-scrollbar class="notifications-list">
3 <div
4 v-for="item of listSanitized"
5 :key="item.id"
6 class="item flex"
7 :class="[{ pointer: !!item.action }, item.type]"
8 @click.stop="item.action ? preformAction(item.id, item.action) : () => {}"
9 >
10 <div class="icon-box">
11 <Icon v-if="item.category === 'alert'" :name="AlertIcon" :size="21" />
12 <n-tooltip v-if="!item.read" trigger="hover" class="p-0!" content-class="p-0!" placement="right">
13 <template #trigger>
14 <div class="read-badge" @click.stop="setRead(item.id)" />
15 </template>
16 <div class="px-2 py-1 text-sm">Set as read</div>
17 </n-tooltip>
18 </div>
19 <div class="content grow">
20 <div class="title">{{ item.title }}</div>
21 <div class="description">{{ item.description }}</div>
22 <div class="footer flex items-center justify-between">
23 <div class="date">
24 {{ formatDatetime(item.date) }}
25 </div>
26 <div v-if="!!item.action" class="action-text">
27 {{ item.actionTitle || "Details" }}
28 </div>
29 </div>
30 </div>
31 <n-tooltip trigger="hover" class="p-0!" content-class="p-0!" placement="left">
32 <template #trigger>
33 <div class="delete-btn" @click.stop="deleteOne(item.id)">
34 <Icon :name="DeleteIcon" :size="14" />
35 </div>
36 </template>
37 <div class="px-2 py-1 text-sm">Delete</div>
38 </n-tooltip>
39 </div>
40 <slot name="last" />
41 <n-empty v-if="!list.length" description="There is no notification" class="h-48 justify-center" />
42 </n-scrollbar>
43 </template>
44
45 <script lang="ts" setup>
46 import _take from "lodash/take"
47 import { NEmpty, NScrollbar, NTooltip } from "naive-ui"
48 import { computed } from "vue"
49 import Icon from "@/components/common/Icon.vue"
50 import { useNotifications } from "@/composables/useNotifications"
51
52 const props = defineProps<{
53 maxItems?: number
54 }>()
55 const DeleteIcon = "carbon:trash-can"
56 const AlertIcon = "mdi:alert-outline"
57
58 const list = useNotifications().list
59
60 const listSanitized = computed(() => {
61 if (props.maxItems) {
62 return _take(list.value, props.maxItems)
63 }
64 return list.value
65 })
66
67 function preformAction(id: string | number, action: () => void) {
68 action()
69 setRead(id)
70 }
71
72 function setRead(id: string | number) {
73 useNotifications().setRead(id)
74 }
75
76 function deleteOne(id: string | number) {
77 useNotifications().deleteOne(id)
78 }
79
80 function formatDatetime(date: Date | string) {
81 return useNotifications().formatDatetime(date)
82 }
83 </script>
84
85 <style lang="scss" scoped>
86 .notifications-list {
87 .item {
88 position: relative;
89 padding: 14px 12px;
90 gap: 4px;
91 transition: background-color 0.2s linear;
92
93 .icon-box {
94 width: 45px;
95 min-width: 45px;
96 display: flex;
97 justify-content: center;
98 position: relative;
99
100 .n-icon {
101 display: flex;
102 justify-content: center;
103 align-items: center;
104 border-radius: 50%;
105 width: 42px;
106 height: 42px;
107 margin-top: 2px;
108 }
109
110 .read-badge {
111 position: absolute;
112 top: 5px;
113 left: 0px;
114 width: 10px;
115 height: 10px;
116 border-radius: 50%;
117 background-color: var(--primary-color);
118 cursor: pointer;
119 }
120 }
121
122 .content {
123 padding: 0 8px;
124 font-size: 14px;
125
126 .title {
127 font-weight: bold;
128 }
129
130 .footer {
131 margin-top: 6px;
132
133 .date {
134 font-size: 12px;
135 opacity: 0.5;
136 }
137 .action-text {
138 font-size: 12px;
139 }
140 }
141 }
142
143 .delete-btn {
144 position: absolute;
145 top: 8px;
146 right: 8px;
147 cursor: pointer;
148 opacity: 0;
149
150 &:hover {
151 color: var(--error-color);
152 }
153 }
154
155 &.success {
156 .icon-box {
157 .n-icon {
158 background-color: rgba(var(--success-color-rgb) / 0.1);
159 color: var(--success-color);
160 }
161 }
162 .action-text {
163 color: var(--success-color);
164 }
165 }
166 &.info {
167 .icon-box {
168 .n-icon {
169 background-color: rgba(var(--info-color-rgb) / 0.1);
170 color: var(--info-color);
171 }
172 }
173 .action-text {
174 color: var(--info-color);
175 }
176 }
177 &.warning {
178 .icon-box {
179 .n-icon {
180 background-color: rgba(var(--warning-color-rgb) / 0.1);
181 color: var(--warning-color);
182 }
183 }
184 .action-text {
185 color: var(--warning-color);
186 }
187 }
188 &.error {
189 .icon-box {
190 .n-icon {
191 background-color: rgba(var(--error-color-rgb) / 0.1);
192 color: var(--error-color);
193 }
194 }
195 .action-text {
196 color: var(--error-color);
197 }
198 }
199
200 &.pointer {
201 cursor: pointer;
202 }
203
204 &:not(:last-child) {
205 border-bottom: 1px solid var(--border-color);
206 }
207
208 &:hover {
209 background-color: var(--hover-color);
210
211 .delete-btn {
212 opacity: 0.5;
213
214 &:hover {
215 opacity: 1;
216 }
217 }
218 }
219 }
220 }
221
222 .direction-rtl {
223 .notifications-list {
224 .item {
225 .delete-btn {
226 right: unset;
227 left: 8px;
228 }
229 }
230 }
231 }
232 </style>