@ozdao/martyrs 0.2.581 → 0.2.582

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/martyrs/src/components/Button/Button.vue.js +1 -1
  2. package/dist/martyrs/src/components/Feed/Carousel.vue.js +1 -1
  3. package/dist/martyrs/src/components/Feed/Feed.vue.js +1 -1
  4. package/dist/martyrs/src/components/Loader/{Loader.vue2.js → Loader.vue.js} +2 -2
  5. package/dist/martyrs/src/components/Loader/Loader.vue.js.map +1 -0
  6. package/dist/martyrs/src/components/LocationMarker/LocationMarker.vue.js +1 -1
  7. package/dist/martyrs/src/components/Media/Media.vue.js +1 -1
  8. package/dist/martyrs/src/components/Menu/{Menu.vue2.js → Menu.vue.js} +2 -2
  9. package/dist/martyrs/src/components/Menu/Menu.vue.js.map +1 -0
  10. package/dist/martyrs/src/components/SelectMulti/{SelectMulti.vue.js → SelectMulti.vue2.js} +2 -2
  11. package/dist/martyrs/src/components/SelectMulti/SelectMulti.vue2.js.map +1 -0
  12. package/dist/martyrs/src/components/Spoiler/{Spoiler.vue2.js → Spoiler.vue.js} +2 -2
  13. package/dist/martyrs/src/components/Spoiler/Spoiler.vue.js.map +1 -0
  14. package/dist/martyrs/src/components/Tab/{Tab.vue2.js → Tab.vue.js} +2 -2
  15. package/dist/martyrs/src/components/Tab/Tab.vue.js.map +1 -0
  16. package/dist/martyrs/src/components/UploadImage/UploadImage.vue.js +1 -1
  17. package/dist/martyrs/src/components/UploadImageMultiple/UploadImageMultiple.vue.js +1 -1
  18. package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.js +1 -1
  19. package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.js +1 -1
  20. package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +2 -2
  21. package/dist/martyrs/src/modules/auth/views/components/pages/ProfileBlogposts.vue.js +1 -1
  22. package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEdit.vue.js +2 -2
  23. package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEdit.vue.js.map +1 -1
  24. package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.js +1 -1
  25. package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js +1 -1
  26. package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.js +1 -1
  27. package/dist/martyrs/src/modules/auth/views/configs/navigation.user.config.js +2 -3
  28. package/dist/martyrs/src/modules/auth/views/configs/navigation.user.config.js.map +1 -1
  29. package/dist/martyrs/src/modules/auth/views/router/users.router.js +1 -2
  30. package/dist/martyrs/src/modules/auth/views/router/users.router.js.map +1 -1
  31. package/dist/martyrs/src/modules/backoffice/components/partials/Sidebar.vue.js +1 -1
  32. package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.js +1 -1
  33. package/dist/martyrs/src/modules/core/views/classes/ws.manager.js +16 -1
  34. package/dist/martyrs/src/modules/core/views/classes/ws.manager.js.map +1 -1
  35. package/dist/martyrs/src/modules/core/views/components/blocks/CardHeader.vue.js +1 -1
  36. package/dist/martyrs/src/modules/core/views/components/blocks/PopupDateSelector.vue.js +1 -1
  37. package/dist/martyrs/src/modules/core/views/components/layouts/Client.vue.js +1 -1
  38. package/dist/martyrs/src/modules/core/views/components/partials/Navigation.vue.js +1 -1
  39. package/dist/martyrs/src/modules/core/views/components/sections/SectionPageTitle.vue.js +1 -1
  40. package/dist/martyrs/src/modules/core/views/utils/vue-app-renderer.js +7 -0
  41. package/dist/martyrs/src/modules/core/views/utils/vue-app-renderer.js.map +1 -1
  42. package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js +2 -2
  43. package/dist/martyrs/src/modules/events/components/pages/Event.vue.js +1 -1
  44. package/dist/martyrs/src/modules/events/components/pages/EventsBackoffice.vue.js +1 -1
  45. package/dist/martyrs/src/modules/events/components/sections/Feed.vue.js +1 -1
  46. package/dist/martyrs/src/modules/events/components/sections/List.vue.js +1 -1
  47. package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +1 -1
  48. package/dist/martyrs/src/modules/landing/components/sections/SectionGuide.vue.js +1 -1
  49. package/dist/martyrs/src/modules/marketplace/views/components/sections/SectionMenu.vue.js +1 -1
  50. package/dist/martyrs/src/modules/music/components/forms/ArtistForm.vue.js +1 -1
  51. package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +1 -1
  52. package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js +1 -1
  53. package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +1 -1
  54. package/dist/martyrs/src/modules/music/components/pages/SearchResults.vue.js +1 -1
  55. package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +1 -1
  56. package/dist/martyrs/src/modules/music/components/pages/TrackCreate.vue.js +1 -1
  57. package/dist/martyrs/src/modules/notifications/components/layouts/NotificationsLayout.vue.js +11 -40
  58. package/dist/martyrs/src/modules/notifications/components/layouts/NotificationsLayout.vue.js.map +1 -1
  59. package/dist/martyrs/src/modules/notifications/components/pages/Notifications.vue.js +26 -21
  60. package/dist/martyrs/src/modules/notifications/components/pages/Notifications.vue.js.map +1 -1
  61. package/dist/martyrs/src/modules/notifications/components/sections/NotificationPreferences.vue.js +54 -48
  62. package/dist/martyrs/src/modules/notifications/components/sections/NotificationPreferences.vue.js.map +1 -1
  63. package/dist/martyrs/src/modules/notifications/components/sections/NotificationsList.vue.js +37 -117
  64. package/dist/martyrs/src/modules/notifications/components/sections/NotificationsList.vue.js.map +1 -1
  65. package/dist/martyrs/src/modules/notifications/notifications.client.js +18 -15
  66. package/dist/martyrs/src/modules/notifications/notifications.client.js.map +1 -1
  67. package/dist/martyrs/src/modules/orders/components/forms/FormSelectCustomer.vue.js +1 -1
  68. package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
  69. package/dist/martyrs/src/modules/orders/components/pages/Orders.vue.js +1 -1
  70. package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.js +1 -1
  71. package/dist/martyrs/src/modules/organizations/components/blocks/CardDepartment.vue.js +1 -1
  72. package/dist/martyrs/src/modules/organizations/components/forms/DepartmentForm.vue.js +1 -1
  73. package/dist/martyrs/src/modules/organizations/components/pages/Department.vue.js +1 -1
  74. package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +1 -1
  75. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +2 -2
  76. package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +2 -2
  77. package/dist/martyrs/src/modules/organizations/components/pages/Organizations.vue.js +1 -1
  78. package/dist/martyrs/src/modules/organizations/components/sections/Documents.vue.js +1 -1
  79. package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.js +2 -2
  80. package/dist/martyrs/src/modules/pages/views/components/blocks/CardPage.vue.js +1 -1
  81. package/dist/martyrs/src/modules/products/components/elements/Image360.vue.js +1 -1
  82. package/dist/martyrs/src/modules/products/components/pages/Categories.vue.js +1 -1
  83. package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +1 -1
  84. package/dist/martyrs/src/modules/products/components/pages/Product.vue.js +2 -2
  85. package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js +2 -2
  86. package/dist/martyrs/src/modules/products/components/pages/Products.vue.js +2 -2
  87. package/dist/martyrs/src/modules/products/components/sections/FilterProducts.vue.js +1 -1
  88. package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.js +1 -1
  89. package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +1 -1
  90. package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js +1 -1
  91. package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.js +1 -1
  92. package/dist/martyrs/src/modules/spots/components/pages/Spot.vue.js +1 -1
  93. package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.js +1 -1
  94. package/dist/style.css +1 -188
  95. package/package.json +1 -1
  96. package/src/modules/auth/views/components/pages/ProfileEdit.vue +1 -1
  97. package/src/modules/auth/views/configs/navigation.user.config.js +10 -10
  98. package/src/modules/auth/views/router/users.router.js +0 -1
  99. package/src/modules/core/views/classes/ws.manager.js +20 -1
  100. package/src/modules/core/views/utils/vue-app-renderer.js +9 -3
  101. package/src/modules/notifications/components/layouts/NotificationsLayout.vue +9 -53
  102. package/src/modules/notifications/components/pages/Notifications.vue +21 -22
  103. package/src/modules/notifications/components/sections/NotificationPreferences.vue +41 -180
  104. package/src/modules/notifications/components/sections/NotificationsList.vue +39 -219
  105. package/src/modules/notifications/notifications.client.js +17 -16
  106. package/dist/martyrs/src/components/Loader/Loader.vue2.js.map +0 -1
  107. package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +0 -1
  108. package/dist/martyrs/src/components/SelectMulti/SelectMulti.vue.js.map +0 -1
  109. package/dist/martyrs/src/components/Spoiler/Spoiler.vue2.js.map +0 -1
  110. package/dist/martyrs/src/components/Tab/Tab.vue2.js.map +0 -1
  111. package/dist/martyrs/src/modules/auth/views/components/blocks/ProfileCard.vue.js +0 -44
  112. package/dist/martyrs/src/modules/auth/views/components/blocks/ProfileCard.vue.js.map +0 -1
@@ -1,69 +1,25 @@
1
1
  <template>
2
- <div class="notifications-layout">
3
- <div class="">
4
- <router-view></router-view>
5
- </div>
6
-
7
- <div v-if="!wsConnected" class="connection-status">
8
- <div class="connection-warning">
9
- <span class="warning-icon">⚠️</span>
10
- <span class="warning-text">
11
- Notification service disconnected.
12
- <button @click="reconnect" class="reconnect-btn">Reconnect</button>
13
- </span>
14
- </div>
2
+ <div>
3
+ <router-view />
4
+
5
+ <!-- Показываем только если WS был подключен и потом отвалился -->
6
+ <div v-if="wsManager.state.wasConnected && !wsManager.state.isConnected" class="pos-fixed pos-b-0 pos-l-0 pos-r-0 pd-small bg-warning t-center">
7
+ <span>⚠️ Notification service disconnected.</span>
8
+ <button class="mn-l-thin bg-none bd-none cursor-pointer t-main" @click="reconnect">Reconnect</button>
15
9
  </div>
16
10
  </div>
17
11
  </template>
18
12
 
19
13
  <script setup>
20
- import { computed, inject, ref, onMounted, onUnmounted } from 'vue';
14
+ import { computed, inject } from 'vue';
21
15
  import wsManager from '@martyrs/src/modules/core/views/classes/ws.manager.js';
22
16
 
23
- // Get notification manager from store
24
17
  const store = inject('store');
25
-
26
18
  const notificationManager = computed(() => store.notificationManager || null);
27
19
 
28
- // Реактивное состояние WebSocket
29
- const wsConnected = ref(wsManager.isConnected);
30
-
31
- // ID слушателей для очистки
32
- const openListenerId = ref(null);
33
- const closeListenerId = ref(null);
34
-
35
- onMounted(() => {
36
- // Подписываемся на события WebSocket
37
- openListenerId.value = wsManager.addEventListener('open', () => {
38
- wsConnected.value = true;
39
- });
40
-
41
- closeListenerId.value = wsManager.addEventListener('close', () => {
42
- wsConnected.value = false;
43
- });
44
-
45
- // Устанавливаем начальное состояние
46
- wsConnected.value = wsManager.isConnected;
47
- });
48
-
49
- onUnmounted(() => {
50
- // Очищаем слушатели
51
- if (openListenerId.value) {
52
- wsManager.removeEventListener('open', openListenerId.value);
53
- }
54
- if (closeListenerId.value) {
55
- wsManager.removeEventListener('close', closeListenerId.value);
56
- }
57
- });
58
-
59
-
60
- // Methods
61
20
  const reconnect = () => {
62
21
  if (notificationManager.value) {
63
22
  notificationManager.value.initialize();
64
23
  }
65
24
  };
66
- </script>
67
-
68
- <style scoped>
69
- </style>
25
+ </script>
@@ -1,36 +1,35 @@
1
1
  <template>
2
- <div class="notifications-page pd-small">
3
- <Tab
4
- v-model:selected="activeTab"
5
- :tabs="[
6
- { label: `All Notifications${unreadCount > 0 ? ` (${unreadCount})` : ''}`, value: 'all' },
7
- { label: 'Notification Settings', value: 'preferences' }
2
+ <div class="mobile:pd-thin pd-medium">
3
+ <SectionPageTitle
4
+ v-if="!MOBILE_APP"
5
+ :title="`Notifications${unreadCount > 0 ? ` (${unreadCount})` : ''}`"
6
+ :actions="[
7
+ { method: () => isSettingsPopup = true, label: 'Settings' }
8
8
  ]"
9
- class="flex-child-default gap-micro scroll-hide bg-light radius-medium h-max pd-thin mn-b-thin o-x-scroll"
10
- classTab="bg-white"
9
+ class="mn-b-small"
11
10
  />
12
-
13
- <div class="tab-content">
14
- <notifications-list v-if="activeTab === 'all'" />
15
- <notification-preferences v-else-if="activeTab === 'preferences'" />
16
- </div>
11
+
12
+ <NotificationsList />
13
+
14
+ <Popup
15
+ :isPopupOpen="isSettingsPopup"
16
+ @close-popup="isSettingsPopup = false"
17
+ title="Notification Settings"
18
+ class="bg-white pd-medium w-m-30r radius-big"
19
+ >
20
+ <NotificationPreferences />
21
+ </Popup>
17
22
  </div>
18
23
  </template>
19
24
 
20
25
  <script setup>
21
26
  import { ref, inject } from 'vue';
22
- import { useRoute } from 'vue-router';
23
- import Tab from "@martyrs/src/components/Tab/Tab.vue";
27
+ import SectionPageTitle from '@martyrs/src/modules/core/views/components/sections/SectionPageTitle.vue';
28
+ import Popup from '@martyrs/src/components/Popup/Popup.vue';
24
29
  import NotificationsList from '../sections/NotificationsList.vue';
25
30
  import NotificationPreferences from '../sections/NotificationPreferences.vue';
26
31
 
27
- // Get route and notification state
28
- const route = useRoute();
29
32
  const { unreadCount } = inject('useNotifications')();
30
33
 
31
- // Set initial active tab based on route query param or default to 'all'
32
- const activeTab = ref(route.query.tab || 'all');
34
+ const isSettingsPopup = ref(false);
33
35
  </script>
34
-
35
- <style scoped>
36
- </style>
@@ -1,51 +1,49 @@
1
1
  <template>
2
- <div class="notification-preferences">
3
- <h2 class="mn-b-small">Notification Preferences</h2>
4
- <p class="description">Choose how you want to receive notifications</p>
5
-
6
- <div v-if="loading" class="preferences-loading">
7
- <div class="loading-spinner">🔄</div>
8
- <p>Loading preferences...</p>
2
+ <div>
3
+ <p class="t-transp mn-b-small">Choose how you want to receive notifications</p>
4
+
5
+ <div v-if="loading" class="pd-large t-center t-transp">
6
+ <Loader />
9
7
  </div>
10
-
11
- <div v-else class="preferences-form">
12
- <div
13
- v-for="(enabled, channelType) in preferences"
14
- :key="channelType"
15
- class="preference-item"
8
+
9
+ <div v-else class="gap-thin flex-column flex">
10
+ <div
11
+ v-for="(enabled, channelType) in preferences"
12
+ :key="channelType"
13
+ class="flex flex-v-center flex-between bg-light radius-small pd-small"
16
14
  >
17
- <div class="preference-info">
18
- <div class="preference-icon">{{ getChannelIcon(channelType) }}</div>
19
- <div class="preference-details">
20
- <h3>{{ getChannelName(channelType) }}</h3>
21
- <p>{{ getChannelDescription(channelType) }}</p>
15
+ <div class="flex w-100 flex-v-center gap-small mn-r-small">
16
+ <div class="h4">{{ getChannelIcon(channelType) }}</div>
17
+ <div class="flex-column flex gap-micro">
18
+ <p class="fw-medium lh-1">{{ getChannelName(channelType) }}</p>
19
+ <p class="p-small t-transp lh-1">{{ getChannelDescription(channelType) }}</p>
22
20
  </div>
23
21
  </div>
24
- <label class="toggle-switch">
25
- <input
26
- type="checkbox"
27
- :checked="enabled"
28
- @change="updatePreference(channelType, $event.target.checked)"
29
- >
30
- <span class="toggle-slider"></span>
31
- </label>
22
+ <Checkbox
23
+ mode="switch"
24
+ :radio="enabled"
25
+ @update:radio="updatePreference(channelType, $event)"
26
+ class="flex-shrink-0"
27
+ />
32
28
  </div>
33
-
34
- <div class="form-actions">
35
- <button
36
- class="save-btn"
37
- :disabled="!hasChanges || saving"
38
- @click="savePreferences"
39
- >
40
- {{ saving ? 'Saving...' : 'Save Changes' }}
41
- </button>
42
- <button
43
- v-if="hasChanges"
44
- class="cancel-btn"
45
- @click="resetChanges"
29
+
30
+ <div class="flex flex-end gap-small mn-t-thin">
31
+ <Button
32
+ v-if="hasChanges"
33
+ :submit="resetChanges"
34
+ :showLoader="false"
35
+ :showSucces="false"
36
+ class="bg-white t-dark"
46
37
  >
47
38
  Cancel
48
- </button>
39
+ </Button>
40
+ <Button
41
+ :submit="savePreferences"
42
+ :validation="!hasChanges || saving"
43
+ class="bg-main t-white"
44
+ >
45
+ {{ saving ? 'Saving...' : 'Save Changes' }}
46
+ </Button>
49
47
  </div>
50
48
  </div>
51
49
  </div>
@@ -53,6 +51,9 @@
53
51
 
54
52
  <script setup>
55
53
  import { ref, reactive, computed, onMounted, inject } from 'vue';
54
+ import Checkbox from '@martyrs/src/components/Checkbox/Checkbox.vue';
55
+ import Button from '@martyrs/src/components/Button/Button.vue';
56
+ import Loader from '@martyrs/src/components/Loader/Loader.vue';
56
57
 
57
58
  // Get notification preferences functionality
58
59
  const {
@@ -154,143 +155,3 @@ onMounted(async () => {
154
155
  });
155
156
  </script>
156
157
 
157
- <style scoped>
158
-
159
- .description {
160
- color: #666;
161
- margin-bottom: 24px;
162
- }
163
-
164
- .preferences-loading {
165
- display: flex;
166
- flex-direction: column;
167
- align-items: center;
168
- justify-content: center;
169
- padding: 48px 0;
170
- }
171
-
172
- .loading-spinner {
173
- font-size: 2rem;
174
- margin-bottom: 16px;
175
- animation: spin 2s linear infinite;
176
- }
177
-
178
- @keyframes spin {
179
- 0% { transform: rotate(0deg); }
180
- 100% { transform: rotate(360deg); }
181
- }
182
-
183
- .preference-item {
184
- display: flex;
185
- align-items: center;
186
- justify-content: space-between;
187
- padding: 16px;
188
- border-bottom: 1px solid #eee;
189
- }
190
-
191
- .preference-item:last-child {
192
- border-bottom: none;
193
- }
194
-
195
- .preference-info {
196
- display: flex;
197
- align-items: center;
198
- flex: 1;
199
- }
200
-
201
- .preference-icon {
202
- font-size: 1.5rem;
203
- margin-right: 16px;
204
- width: 40px;
205
- height: 40px;
206
- display: flex;
207
- align-items: center;
208
- justify-content: center;
209
- }
210
-
211
- .preference-details h3 {
212
- margin: 0 0 4px 0;
213
- font-size: 1rem;
214
- }
215
-
216
- .preference-details p {
217
- margin: 0;
218
- color: #666;
219
- font-size: 0.875rem;
220
- }
221
-
222
- /* Toggle switch styles */
223
- .toggle-switch {
224
- position: relative;
225
- display: inline-block;
226
- width: 50px;
227
- height: 24px;
228
- }
229
-
230
- .toggle-switch input {
231
- opacity: 0;
232
- width: 0;
233
- height: 0;
234
- }
235
-
236
- .toggle-slider {
237
- position: absolute;
238
- cursor: pointer;
239
- top: 0;
240
- left: 0;
241
- right: 0;
242
- bottom: 0;
243
- background-color: #ccc;
244
- transition: .4s;
245
- border-radius: 24px;
246
- }
247
-
248
- .toggle-slider:before {
249
- position: absolute;
250
- content: "";
251
- height: 16px;
252
- width: 16px;
253
- left: 4px;
254
- bottom: 4px;
255
- background-color: white;
256
- transition: .4s;
257
- border-radius: 50%;
258
- }
259
-
260
- input:checked + .toggle-slider {
261
- background-color: #2196F3;
262
- }
263
-
264
- input:checked + .toggle-slider:before {
265
- transform: translateX(26px);
266
- }
267
-
268
- .form-actions {
269
- margin-top: 24px;
270
- display: flex;
271
- justify-content: flex-end;
272
- gap: 16px;
273
- }
274
-
275
- .save-btn, .cancel-btn {
276
- padding: 8px 16px;
277
- border-radius: 4px;
278
- cursor: pointer;
279
- }
280
-
281
- .save-btn {
282
- background-color: #2196F3;
283
- border: none;
284
- color: white;
285
- }
286
-
287
- .save-btn:disabled {
288
- background-color: #ccc;
289
- cursor: not-allowed;
290
- }
291
-
292
- .cancel-btn {
293
- background-color: white;
294
- border: 1px solid #ccc;
295
- }
296
- </style>
@@ -1,111 +1,52 @@
1
1
  <template>
2
- <div class="notifications-list-container">
3
- <div v-if="!MOBILE_APP" class="notifications-header">
4
- <h2>Notifications</h2>
5
- <!-- <div class="notifications-controls">
6
- <div class="notifications-filter">
7
- <label for="filter-type">Filter: </label>
8
- <select id="filter-type" v-model="filterType">
9
- <option value="all">All</option>
10
- <option value="unread">Unread</option>
11
- <option value="read">Read</option>
12
- <option value="info">Info</option>
13
- <option value="success">Success</option>
14
- <option value="warning">Warning</option>
15
- <option value="error">Error</option>
16
- </select>
17
- </div>
18
- <button
19
- v-if="unreadCount > 0"
20
- class="mark-all-read-btn"
21
- @click="markAllAsRead"
22
- >
23
- Mark all as read
24
- </button>
25
- </div> -->
26
- </div>
27
-
28
- <div v-if="loading" class="notifications-loading">
29
- <div class="loading-spinner">🔄</div>
30
- <p>Loading notifications...</p>
31
- </div>
32
-
33
- <div v-else-if="filteredNotifications.length === 0" class="notifications-empty">
34
- <p>{{ emptyMessage }}</p>
35
- </div>
36
-
37
- <div v-else class="notifications-items">
38
- <notification-item
39
- v-for="notification in filteredNotifications"
40
- :key="notification._id"
41
- :notification="notification"
42
- @click="handleNotificationClick(notification)"
43
- />
44
- </div>
45
-
46
- <div v-if="!loading && notifications.length > 0" class="notifications-footer">
47
- <button v-if="lastSync" class="refresh-btn" @click="refreshNotifications">
48
- 🔄 Last updated: {{ formatTime(lastSync) }}
49
- </button>
50
- </div>
51
- </div>
2
+ <Feed
3
+ :states="{
4
+ empty: {
5
+ title: 'No Notifications',
6
+ description: 'You have no notifications yet'
7
+ }
8
+ }"
9
+ :store="{
10
+ read: fetchNotifications
11
+ }"
12
+ :options="{
13
+ limit: 20
14
+ }"
15
+ :skeleton="{
16
+ structure: [
17
+ { block: 'text', size: 'small' },
18
+ { block: 'text', size: 'large' }
19
+ ]
20
+ }"
21
+ v-slot="{ items }"
22
+ class="gap-thin cols-1"
23
+ >
24
+ <NotificationItem
25
+ v-for="notification in items"
26
+ :key="notification._id"
27
+ :notification="notification"
28
+ @click="handleNotificationClick(notification)"
29
+ />
30
+ </Feed>
52
31
  </template>
53
32
 
54
33
  <script setup>
55
- import { ref, computed, onMounted, inject } from 'vue';
56
- import { useRouter } from 'vue-router';
34
+ import { inject } from 'vue';
35
+ import Feed from '@martyrs/src/components/Feed/Feed.vue';
57
36
  import NotificationItem from '../blocks/NotificationItem.vue';
58
37
  import * as auth from '@martyrs/src/modules/auth/views/store/auth.js';
38
+ import { actions } from '@martyrs/src/modules/notifications/store/notifications.store.js';
59
39
 
60
- // Get router and notification functionality
61
- const router = useRouter();
62
- const {
63
- notifications,
64
- unreadCount,
65
- loading,
66
- lastSync,
67
- markAllAsRead,
68
- getNotifications,
69
- handleNotificationAction
70
- } = inject('useNotifications')();
40
+ const { handleNotificationAction } = inject('useNotifications')();
71
41
 
72
- // Local state
73
- const filterType = ref('all');
74
-
75
- // Computed properties
76
- const filteredNotifications = computed(() => {
77
- let filtered = [...notifications.value];
78
-
79
- // Apply filters
80
- if (filterType.value === 'unread') {
81
- filtered = filtered.filter(n => n.status !== 'read');
82
- } else if (filterType.value === 'read') {
83
- filtered = filtered.filter(n => n.status === 'read');
84
- } else if (filterType.value !== 'all') {
85
- // Filter by notification type
86
- filtered = filtered.filter(n => n.type === filterType.value);
87
- }
88
-
89
- // Sort by creation date (newest first)
90
- return filtered.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
91
- });
42
+ const fetchNotifications = async (options) => {
43
+ const userId = auth.state.user._id;
44
+ if (!userId) return [];
92
45
 
93
- const emptyMessage = computed(() => {
94
- if (notifications.value.length === 0) {
95
- return 'You have no notifications';
96
- }
97
-
98
- switch (filterType.value) {
99
- case 'unread':
100
- return 'You have no unread notifications';
101
- case 'read':
102
- return 'You have no read notifications';
103
- default:
104
- return `You have no ${filterType.value} notifications`;
105
- }
106
- });
46
+ const data = await actions.getNotifications(userId);
47
+ return data || [];
48
+ };
107
49
 
108
- // Methods
109
50
  const handleNotificationClick = (notification) => {
110
51
  if (notification._id) {
111
52
  handleNotificationAction({
@@ -114,125 +55,4 @@ const handleNotificationClick = (notification) => {
114
55
  });
115
56
  }
116
57
  };
117
-
118
- const refreshNotifications = () => {
119
- const userId = auth.state.user._id;
120
- if (userId) {
121
- getNotifications(userId);
122
- }
123
- };
124
-
125
- // Format relative time without external libraries
126
- const formatTime = (timestamp) => {
127
- if (!timestamp) return '';
128
-
129
- const now = new Date();
130
- const date = new Date(timestamp);
131
- const diffSeconds = Math.floor((now - date) / 1000);
132
-
133
- // Format based on how long ago
134
- if (diffSeconds < 60) {
135
- return 'Just now';
136
- } else if (diffSeconds < 3600) {
137
- const minutes = Math.floor(diffSeconds / 60);
138
- return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;
139
- } else if (diffSeconds < 86400) {
140
- const hours = Math.floor(diffSeconds / 3600);
141
- return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;
142
- } else {
143
- // Format date string
144
- return date.toLocaleDateString(undefined, {
145
- year: 'numeric',
146
- month: 'short',
147
- day: 'numeric',
148
- hour: '2-digit',
149
- minute: '2-digit'
150
- });
151
- }
152
- };
153
-
154
- // Lifecycle
155
- onMounted(() => {
156
- const userId = auth.state.user._id;
157
- if (userId && notifications.value.length === 0 && !loading.value) {
158
- getNotifications(userId);
159
- }
160
- });
161
58
  </script>
162
-
163
- <style scoped>
164
- .notifications-list-container {
165
- width: 100%;
166
- max-width: 800px;
167
- margin: 0 auto;
168
- }
169
-
170
- .notifications-header {
171
- display: flex;
172
- justify-content: space-between;
173
- align-items: center;
174
- margin-bottom: 20px;
175
- }
176
-
177
- .notifications-controls {
178
- display: flex;
179
- align-items: center;
180
- gap: 16px;
181
- }
182
-
183
- .notifications-filter select {
184
- padding: 6px 12px;
185
- border-radius: 4px;
186
- border: 1px solid #ddd;
187
- }
188
-
189
- .mark-all-read-btn {
190
- background-color: #2196f3;
191
- color: white;
192
- border: none;
193
- padding: 6px 12px;
194
- border-radius: 4px;
195
- cursor: pointer;
196
- }
197
-
198
- .notifications-loading,
199
- .notifications-empty {
200
- padding: 40px 0;
201
- text-align: center;
202
- color: #666;
203
- }
204
-
205
- .loading-spinner {
206
- font-size: 2rem;
207
- margin-bottom: 10px;
208
- animation: spin 1s linear infinite;
209
- }
210
-
211
- @keyframes spin {
212
- from { transform: rotate(0deg); }
213
- to { transform: rotate(360deg); }
214
- }
215
-
216
- .notifications-items {
217
- display: flex;
218
- flex-direction: column;
219
- gap: 10px;
220
- }
221
-
222
- .notifications-footer {
223
- margin-top: 20px;
224
- text-align: center;
225
- }
226
-
227
- .refresh-btn {
228
- background: none;
229
- border: none;
230
- color: #666;
231
- cursor: pointer;
232
- padding: 6px 12px;
233
- }
234
-
235
- .refresh-btn:hover {
236
- color: #2196f3;
237
- }
238
- </style>