@apptegy/nuxt-navigation 0.1.118 → 0.1.119

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.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=3.0.0"
5
5
  },
6
6
  "configKey": "@apptegy/nuxt-navigation",
7
- "version": "0.1.118",
7
+ "version": "0.1.119",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.1",
10
10
  "unbuild": "unknown"
@@ -70,8 +70,10 @@
70
70
  :loading="notificationsLoading"
71
71
  :active-tab-id="activeTabId"
72
72
  :active-filter-id="activeFilterId"
73
+ :filter-catalog="filterCatalog"
73
74
  @update:active-tab-id="handleTabChange"
74
75
  @update:active-filter-id="handleFilterChange"
76
+ @update:filter-catalog="filterCatalog = $event"
75
77
  @clear-all="(payload) => $emit('notifications-clear-all', payload)"
76
78
  @dismiss="(payload) => $emit('notifications-dismiss', payload)"
77
79
  @select="handleNotificationSelect"
@@ -90,8 +92,10 @@
90
92
  :loading="notificationsLoading"
91
93
  :active-tab-id="activeTabId"
92
94
  :active-filter-id="activeFilterId"
95
+ :filter-catalog="filterCatalog"
93
96
  @update:active-tab-id="handleTabChange"
94
97
  @update:active-filter-id="handleFilterChange"
98
+ @update:filter-catalog="filterCatalog = $event"
95
99
  @clear-all="(payload) => $emit('notifications-clear-all', payload)"
96
100
  @dismiss="(payload) => $emit('notifications-dismiss', payload)"
97
101
  @select="handleNotificationSelect"
@@ -173,6 +177,7 @@ const notificationsLoading = computed(() => props.notifications?.loading ?? fals
173
177
  const panelOpen = ref(props.notifications?.panelOpen ?? false);
174
178
  const activeTabId = ref(props.notifications?.activeTabId || "new");
175
179
  const activeFilterId = ref(props.notifications?.activeFilterId || "all-types");
180
+ const filterCatalog = ref([]);
176
181
  const popoverPt = {
177
182
  content: {
178
183
  class: "notifications-popover__content",
@@ -236,16 +241,8 @@ watch(
236
241
  }
237
242
  );
238
243
  watch(panelOpen, (value) => {
239
- if (value) {
240
- patchNotifications({ panelOpen: true });
241
- return;
242
- }
243
- activeFilterId.value = "all-types";
244
- patchNotifications({
245
- panelOpen: false,
246
- activeFilterId: "all-types"
247
- });
248
- if (!isMobilePanel.value) {
244
+ patchNotifications({ panelOpen: value });
245
+ if (!value && !isMobilePanel.value) {
249
246
  popoverRef.value?.hide();
250
247
  }
251
248
  });
@@ -212,14 +212,18 @@ const props = defineProps({
212
212
  loading: { type: Boolean, required: false, default: false },
213
213
  activeTabId: { type: String, required: false },
214
214
  activeFilterId: { type: String, required: false },
215
+ filterCatalog: { type: Array, required: false, default: () => [] },
215
216
  embedded: { type: Boolean, required: false, default: false },
216
217
  mobile: { type: Boolean, required: false, default: false }
217
218
  });
218
- const emit = defineEmits(["update:activeTabId", "update:activeFilterId", "clear-all", "dismiss", "select", "close"]);
219
+ const emit = defineEmits(["update:activeTabId", "update:activeFilterId", "update:filterCatalog", "clear-all", "dismiss", "select", "close"]);
219
220
  const filtersRef = ref();
220
221
  const activeTabId = ref(props.activeTabId || props.data.tabs[0]?.id || "");
221
222
  const activeFilterId = ref(props.activeFilterId || props.data.filters?.[0]?.id || "all-types");
222
- const stickyFilters = ref([]);
223
+ const stickyFilters = ref([...props.filterCatalog ?? []]);
224
+ function filterCatalogKey(filters) {
225
+ return filters.map((filter) => filter.id).join("|");
226
+ }
223
227
  watch(
224
228
  () => props.activeTabId,
225
229
  (value) => {
@@ -236,6 +240,18 @@ watch(
236
240
  }
237
241
  }
238
242
  );
243
+ watch(
244
+ () => props.filterCatalog,
245
+ (catalog) => {
246
+ if (!catalog?.length) {
247
+ return;
248
+ }
249
+ if (filterCatalogKey(stickyFilters.value) !== filterCatalogKey(catalog)) {
250
+ stickyFilters.value = [...catalog];
251
+ }
252
+ },
253
+ { deep: true }
254
+ );
239
255
  watch(
240
256
  () => props.data.tabs,
241
257
  (tabs) => {
@@ -250,9 +266,6 @@ const usesDynamicFilters = computed(() => !props.data.filters?.length);
250
266
  const activeTab = computed(
251
267
  () => props.data.tabs.find((tab) => tab.id === activeTabId.value)
252
268
  );
253
- function filterCatalogKey(filters) {
254
- return filters.map((filter) => filter.id).join("|");
255
- }
256
269
  watch(
257
270
  [allNotifications, activeTab, activeFilterId, () => props.data.filters],
258
271
  () => {
@@ -265,6 +278,7 @@ watch(
265
278
  });
266
279
  if (filterCatalogKey(stickyFilters.value) !== filterCatalogKey(nextStickyFilters)) {
267
280
  stickyFilters.value = nextStickyFilters;
281
+ emit("update:filterCatalog", [...nextStickyFilters]);
268
282
  }
269
283
  },
270
284
  { immediate: true, deep: true }
@@ -1,9 +1,14 @@
1
- import type { INotificationsPanelData } from '../../types/index.js';
1
+ import type { INotificationsPanelData, INotificationFilter } from '../../types/index.js';
2
2
  type __VLS_Props = {
3
3
  data: INotificationsPanelData;
4
4
  loading?: boolean;
5
5
  activeTabId?: string;
6
6
  activeFilterId?: string;
7
+ /**
8
+ * Last "all types" pill catalog, owned by the header so it survives panel remount.
9
+ * Lets a kind filter stay selected on reopen without inventing the full default catalog.
10
+ */
11
+ filterCatalog?: INotificationFilter[];
7
12
  embedded?: boolean;
8
13
  mobile?: boolean;
9
14
  };
@@ -15,6 +20,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
15
20
  }) => any;
16
21
  "update:activeTabId": (id: string) => any;
17
22
  "update:activeFilterId": (id: string) => any;
23
+ "update:filterCatalog": (filters: INotificationFilter[]) => any;
18
24
  "clear-all": (payload: {
19
25
  groupId: string;
20
26
  tabId: string;
@@ -34,6 +40,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
34
40
  }) => any) | undefined;
35
41
  "onUpdate:activeTabId"?: ((id: string) => any) | undefined;
36
42
  "onUpdate:activeFilterId"?: ((id: string) => any) | undefined;
43
+ "onUpdate:filterCatalog"?: ((filters: INotificationFilter[]) => any) | undefined;
37
44
  "onClear-all"?: ((payload: {
38
45
  groupId: string;
39
46
  tabId: string;
@@ -47,6 +54,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
47
54
  onClose?: (() => any) | undefined;
48
55
  }>, {
49
56
  loading: boolean;
57
+ filterCatalog: INotificationFilter[];
50
58
  embedded: boolean;
51
59
  mobile: boolean;
52
60
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -19,6 +19,11 @@ export interface INotificationApiRecord {
19
19
  detail_link?: string | null;
20
20
  notification_state: NotificationApiState | string;
21
21
  created_at: string;
22
+ /**
23
+ * Most recent activity on the notification (e.g. new reaction/comment).
24
+ * Prefer this over `created_at` for relative time and date grouping.
25
+ */
26
+ last_activity_at?: string | null;
22
27
  }
23
28
  export interface INotificationsPagination {
24
29
  page: number;
@@ -48,7 +53,10 @@ export interface INotificationItem {
48
53
  actors: INotificationActor[];
49
54
  actionText: string;
50
55
  timestampLabel: string;
51
- /** ISO-8601 timestamp from the API — used to derive group labels and relative time. */
56
+ /**
57
+ * ISO-8601 timestamp used for group labels and relative time.
58
+ * Derived from API `last_activity_at`, falling back to `created_at`.
59
+ */
52
60
  createdAt?: string;
53
61
  contextLabel?: string;
54
62
  /** When false, the notification counts toward the bell badge and appears in the New tab. */
@@ -5,6 +5,11 @@ export declare const DEFAULT_NOTIFICATION_TABS: INotificationTab[];
5
5
  export declare function normalizeNotificationKind(value: string): string;
6
6
  export declare function mapSourceRecordType(sourceRecordType: string): NotificationType;
7
7
  export declare function mapNotificationKind(kind: string | null | undefined, fallbackType: NotificationType): string;
8
+ /** Prefer `last_activity_at` for display/grouping; fall back to `created_at`. */
9
+ export declare function resolveNotificationActivityAt(record: {
10
+ created_at: string;
11
+ last_activity_at?: string | null;
12
+ }): string;
8
13
  export declare function mapNotificationApiActor(actor: INotificationApiActor): {
9
14
  name: string;
10
15
  avatarUrl: string | undefined;
@@ -32,6 +32,9 @@ export function mapNotificationKind(kind, fallbackType) {
32
32
  }
33
33
  return normalizeNotificationKind(kind) || normalizeNotificationKind(fallbackType);
34
34
  }
35
+ export function resolveNotificationActivityAt(record) {
36
+ return record.last_activity_at || record.created_at;
37
+ }
35
38
  export function mapNotificationApiActor(actor) {
36
39
  const name = [actor.first_name, actor.last_name].filter(Boolean).join(" ").trim();
37
40
  return {
@@ -42,13 +45,14 @@ export function mapNotificationApiActor(actor) {
42
45
  export function mapNotificationApiRecord(record, options) {
43
46
  const state = record.notification_state;
44
47
  const type = mapSourceRecordType(record.source_record_type);
48
+ const activityAt = resolveNotificationActivityAt(record);
45
49
  return {
46
50
  id: String(record.id),
47
51
  type,
48
52
  kind: mapNotificationKind(record.kind, type),
49
53
  actors: (record.actors ?? []).map(mapNotificationApiActor),
50
54
  actionText: record.title,
51
- timestampLabel: formatNotificationTimeAgo(record.created_at, {
55
+ timestampLabel: formatNotificationTimeAgo(activityAt, {
52
56
  locale: options.locale,
53
57
  now: options.now,
54
58
  labels: {
@@ -56,7 +60,7 @@ export function mapNotificationApiRecord(record, options) {
56
60
  yesterdayTimeAgo: options.dateLabels.yesterdayTimeAgo
57
61
  }
58
62
  }),
59
- createdAt: record.created_at,
63
+ createdAt: activityAt,
60
64
  contextLabel: record.summary,
61
65
  dismissed: state === "dismissed",
62
66
  read: state === "read",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apptegy/nuxt-navigation",
3
- "version": "0.1.118",
3
+ "version": "0.1.119",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {