@apptegy/nuxt-navigation 0.1.114 → 0.1.115

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.114",
7
+ "version": "0.1.115",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.1",
10
10
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { defineNuxtModule, createResolver, addComponentsDir, addImportsDir } from '@nuxt/kit';
2
2
 
3
- const module$1 = defineNuxtModule({
3
+ const module = defineNuxtModule({
4
4
  meta: {
5
5
  name: "@apptegy/nuxt-navigation",
6
6
  compatibility: {
@@ -22,4 +22,4 @@ const module$1 = defineNuxtModule({
22
22
  }
23
23
  });
24
24
 
25
- export { module$1 as default };
25
+ export { module as default };
@@ -29,7 +29,27 @@ type __VLS_Slots = {} & {
29
29
  } & {
30
30
  notifications?: (props: typeof __VLS_15) => any;
31
31
  };
32
- declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, any, string, import("vue").PublicProps, any, {
32
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
33
+ "selected-option": (...args: any[]) => void;
34
+ "user-logout": (...args: any[]) => void;
35
+ "update:notificationPanelOpen": (...args: any[]) => void;
36
+ "update:activeTabId": (...args: any[]) => void;
37
+ "update:activeFilterId": (...args: any[]) => void;
38
+ "notifications-clear-all": (...args: any[]) => void;
39
+ "notifications-dismiss": (...args: any[]) => void;
40
+ "notifications-select": (...args: any[]) => void;
41
+ "notification-click": (...args: any[]) => void;
42
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
43
+ "onSelected-option"?: ((...args: any[]) => any) | undefined;
44
+ "onUser-logout"?: ((...args: any[]) => any) | undefined;
45
+ "onUpdate:notificationPanelOpen"?: ((...args: any[]) => any) | undefined;
46
+ "onUpdate:activeTabId"?: ((...args: any[]) => any) | undefined;
47
+ "onUpdate:activeFilterId"?: ((...args: any[]) => any) | undefined;
48
+ "onNotifications-clear-all"?: ((...args: any[]) => any) | undefined;
49
+ "onNotifications-dismiss"?: ((...args: any[]) => any) | undefined;
50
+ "onNotifications-select"?: ((...args: any[]) => any) | undefined;
51
+ "onNotification-click"?: ((...args: any[]) => any) | undefined;
52
+ }>, {
33
53
  showNotifications: boolean;
34
54
  notificationsResponse: INotificationsApiResponse | null;
35
55
  notificationsLoading: boolean;
@@ -38,10 +38,6 @@
38
38
 
39
39
  <div class="notification-item__content">
40
40
  <p class="notification-item__message">
41
- <strong>{{ primaryActor.name }}</strong>
42
- <template v-if="additionalActorsLabel">
43
- {{ additionalActorsLabel }}
44
- </template>
45
41
  {{ item.actionText }}
46
42
  </p>
47
43
  <p class="notification-item__meta">
@@ -106,16 +102,6 @@ const groupBadgeLabel = computed(() => {
106
102
  const count = props.item.groupCount ?? Math.max(props.item.actors.length - 1, 0);
107
103
  return count > 0 ? `+${count}` : "";
108
104
  });
109
- const additionalActorsLabel = computed(() => {
110
- if (props.item.groupCount && props.item.groupCount > 0) {
111
- return ` and ${props.item.groupCount} others`;
112
- }
113
- if (props.item.actors.length <= 1) {
114
- return "";
115
- }
116
- const names = props.item.actors.slice(1).map((actor) => actor.name);
117
- return `, ${names.join(", ")}`;
118
- });
119
105
  function svgIcon(paths) {
120
106
  return h(
121
107
  "svg",
@@ -220,7 +206,7 @@ function handleDismiss(event) {
220
206
  text-align: left;
221
207
  color: inherit;
222
208
  }
223
- .notification-item__main:hover .notification-item__message strong {
209
+ .notification-item__main:hover .notification-item__message {
224
210
  color: var(--purple-60, #662e80);
225
211
  }
226
212
  .notification-item__main:focus-visible {
@@ -297,9 +283,6 @@ function handleDismiss(event) {
297
283
  line-height: 20px;
298
284
  color: var(--grey-90, #111827);
299
285
  }
300
- .notification-item__message strong {
301
- font-weight: 600;
302
- }
303
286
  .notification-item__meta {
304
287
  display: flex;
305
288
  flex-wrap: wrap;
@@ -3,10 +3,10 @@ type __VLS_Props = {
3
3
  item: INotificationItem;
4
4
  };
5
5
  declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
- dismiss: (id: string) => any;
7
6
  select: (item: INotificationItem) => any;
7
+ dismiss: (id: string) => any;
8
8
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
9
- onDismiss?: ((id: string) => any) | undefined;
10
9
  onSelect?: ((item: INotificationItem) => any) | undefined;
10
+ onDismiss?: ((id: string) => any) | undefined;
11
11
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  export default _default;
@@ -8,6 +8,11 @@ type __VLS_Props = {
8
8
  mobile?: boolean;
9
9
  };
10
10
  declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ select: (payload: {
12
+ item: INotificationsPanelData["groups"][number]["notifications"][number];
13
+ tabId: string;
14
+ filterId: string;
15
+ }) => any;
11
16
  "update:activeTabId": (id: string) => any;
12
17
  "update:activeFilterId": (id: string) => any;
13
18
  "clear-all": (payload: {
@@ -20,13 +25,13 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
20
25
  tabId: string;
21
26
  filterId: string;
22
27
  }) => any;
23
- select: (payload: {
28
+ close: () => any;
29
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
30
+ onSelect?: ((payload: {
24
31
  item: INotificationsPanelData["groups"][number]["notifications"][number];
25
32
  tabId: string;
26
33
  filterId: string;
27
- }) => any;
28
- close: () => any;
29
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
34
+ }) => any) | undefined;
30
35
  "onUpdate:activeTabId"?: ((id: string) => any) | undefined;
31
36
  "onUpdate:activeFilterId"?: ((id: string) => any) | undefined;
32
37
  "onClear-all"?: ((payload: {
@@ -39,11 +44,6 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
39
44
  tabId: string;
40
45
  filterId: string;
41
46
  }) => any) | undefined;
42
- onSelect?: ((payload: {
43
- item: INotificationsPanelData["groups"][number]["notifications"][number];
44
- tabId: string;
45
- filterId: string;
46
- }) => any) | undefined;
47
47
  onClose?: (() => any) | undefined;
48
48
  }>, {
49
49
  loading: boolean;
@@ -1,5 +1,5 @@
1
1
  export declare function useSidebar(): {
2
- state: any;
2
+ state: import("vue").Ref<boolean, boolean>;
3
3
  toggleSidebar: () => void;
4
4
  sidebarWidth: import("vue").ComputedRef<"264px" | "0px">;
5
5
  };
@@ -6,13 +6,13 @@ export declare const NOTIFICATION_BADGE_COLOR = "#C84B31";
6
6
  export declare const NOTIFICATION_FILTER_CATEGORIES: Array<{
7
7
  id: string;
8
8
  label: string;
9
- types: NotificationType[];
9
+ kinds: string[];
10
10
  }>;
11
11
  /** @deprecated Use NOTIFICATION_FILTER_CATEGORIES */
12
12
  export declare const DEFAULT_NOTIFICATION_FILTERS: ({
13
13
  id: string;
14
14
  label: string;
15
- types: NotificationType[];
15
+ kinds: string[];
16
16
  } | {
17
17
  id: string;
18
18
  label: string;
@@ -14,32 +14,32 @@ export const NOTIFICATION_FILTER_CATEGORIES = [
14
14
  {
15
15
  id: "announcements",
16
16
  label: "Announcements",
17
- types: ["announcement"]
17
+ kinds: ["announcement"]
18
18
  },
19
19
  {
20
20
  id: "messages",
21
21
  label: "Messages",
22
- types: ["message"]
22
+ kinds: ["message"]
23
23
  },
24
24
  {
25
- id: "comments-mentions",
26
- label: "Comments & Mentions",
27
- types: ["comment", "mention", "reply", "reaction"]
25
+ id: "comments-reactions",
26
+ label: "Comments & Reactions",
27
+ kinds: ["comment", "reaction"]
28
28
  },
29
29
  {
30
30
  id: "school-posts",
31
31
  label: "School Posts",
32
- types: ["school-post"]
32
+ kinds: ["school_post"]
33
33
  },
34
34
  {
35
35
  id: "system",
36
36
  label: "System",
37
- types: ["system"]
37
+ kinds: ["system"]
38
38
  },
39
39
  {
40
40
  id: "other",
41
41
  label: "Other",
42
- types: ["unknown"]
42
+ kinds: ["unknown"]
43
43
  }
44
44
  ];
45
45
  export const DEFAULT_NOTIFICATION_FILTERS = [
@@ -7,6 +7,11 @@ export interface INotificationApiActor {
7
7
  }
8
8
  export interface INotificationApiRecord {
9
9
  id: number | string;
10
+ /**
11
+ * Category used for filter pills / `filter[kind][]` (e.g. announcement, comment, reaction).
12
+ * Distinct from `source_record_type`, which describes the underlying record.
13
+ */
14
+ kind?: string | null;
10
15
  source_record_type: string;
11
16
  title: string;
12
17
  summary?: string;
@@ -33,7 +38,13 @@ export interface INotificationActor {
33
38
  }
34
39
  export interface INotificationItem {
35
40
  id: string;
41
+ /** Visual/icon type — derived from `source_record_type`. */
36
42
  type: NotificationType;
43
+ /**
44
+ * Filter category — derived from API `kind` (falls back to `type` when kind is absent).
45
+ * Used by filter pills and `filter[kind][]`.
46
+ */
47
+ kind: string;
37
48
  actors: INotificationActor[];
38
49
  actionText: string;
39
50
  timestampLabel: string;
@@ -67,7 +78,15 @@ export interface INotificationFilter {
67
78
  id: string;
68
79
  label: string;
69
80
  count?: number;
70
- /** Notification types included in this filter. Omit or leave empty to include all types. */
81
+ /**
82
+ * Notification kinds included in this filter (`filter[kind][]`).
83
+ * Omit or leave empty to include all kinds.
84
+ */
85
+ kinds?: string[];
86
+ /**
87
+ * @deprecated Prefer `kinds`. Kept for callers that still filter by
88
+ * `source_record_type`-derived `type`.
89
+ */
71
90
  types?: NotificationType[];
72
91
  }
73
92
  export interface INotificationsPanelData {
@@ -1,7 +1,10 @@
1
1
  import type { INotificationApiActor, INotificationApiRecord, INotificationGroup, INotificationItem, INotificationsApiResponse, INotificationsPanelData, INotificationTab, NotificationType } from '../types/index.js';
2
2
  import { type INotificationDateLabels } from './notification-dates.js';
3
3
  export declare const DEFAULT_NOTIFICATION_TABS: INotificationTab[];
4
+ /** Normalize kind values for filter matching and `filter[kind][]` query params. */
5
+ export declare function normalizeNotificationKind(value: string): string;
4
6
  export declare function mapSourceRecordType(sourceRecordType: string): NotificationType;
7
+ export declare function mapNotificationKind(kind: string | null | undefined, fallbackType: NotificationType): string;
5
8
  export declare function mapNotificationApiActor(actor: INotificationApiActor): {
6
9
  name: string;
7
10
  avatarUrl: string | undefined;
@@ -19,10 +19,19 @@ export const DEFAULT_NOTIFICATION_TABS = [
19
19
  { id: "new", label: "New", unreadOnly: true },
20
20
  { id: "all", label: "All" }
21
21
  ];
22
+ export function normalizeNotificationKind(value) {
23
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/-/g, "_");
24
+ }
22
25
  export function mapSourceRecordType(sourceRecordType) {
23
26
  const normalizedSourceRecordType = sourceRecordType.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
24
27
  return SOURCE_RECORD_TYPE_MAP[normalizedSourceRecordType] ?? "unknown";
25
28
  }
29
+ export function mapNotificationKind(kind, fallbackType) {
30
+ if (!kind) {
31
+ return normalizeNotificationKind(fallbackType);
32
+ }
33
+ return normalizeNotificationKind(kind) || normalizeNotificationKind(fallbackType);
34
+ }
26
35
  export function mapNotificationApiActor(actor) {
27
36
  const name = [actor.first_name, actor.last_name].filter(Boolean).join(" ").trim();
28
37
  return {
@@ -32,9 +41,11 @@ export function mapNotificationApiActor(actor) {
32
41
  }
33
42
  export function mapNotificationApiRecord(record, options) {
34
43
  const state = record.notification_state;
44
+ const type = mapSourceRecordType(record.source_record_type);
35
45
  return {
36
46
  id: String(record.id),
37
- type: mapSourceRecordType(record.source_record_type),
47
+ type,
48
+ kind: mapNotificationKind(record.kind, type),
38
49
  actors: (record.actors ?? []).map(mapNotificationApiActor),
39
50
  actionText: record.title,
40
51
  timestampLabel: formatNotificationTimeAgo(record.created_at, {
@@ -1,4 +1,5 @@
1
1
  import { NOTIFICATION_FILTER_CATEGORIES } from "../constants/notification-types.js";
2
+ import { normalizeNotificationKind } from "./notification-api.js";
2
3
  export function flattenNotifications(groups) {
3
4
  return groups.flatMap((group) => group.notifications);
4
5
  }
@@ -23,11 +24,19 @@ export function getInBadgeNotifications(notifications) {
23
24
  export function getInBadgeCount(notifications) {
24
25
  return getInBadgeNotifications(notifications).length;
25
26
  }
27
+ function filterKinds(filter) {
28
+ if (filter.kinds?.length) {
29
+ return filter.kinds.map(normalizeNotificationKind);
30
+ }
31
+ return (filter.types ?? []).map((type) => normalizeNotificationKind(type));
32
+ }
26
33
  export function notificationMatchesFilter(notification, filter) {
27
- if (!filter.types?.length) {
34
+ const kinds = filterKinds(filter);
35
+ if (!kinds.length) {
28
36
  return true;
29
37
  }
30
- return filter.types.includes(notification.type);
38
+ const notificationKind = normalizeNotificationKind(notification.kind || notification.type);
39
+ return kinds.includes(notificationKind);
31
40
  }
32
41
  export function getTabBadgeCount(notifications, tab) {
33
42
  if (tab.unreadOnly || tab.id === "new") {
@@ -36,7 +45,7 @@ export function getTabBadgeCount(notifications, tab) {
36
45
  return 0;
37
46
  }
38
47
  export function getFilterBadgeCount(notifications, filter) {
39
- if (!filter.types?.length || filter.id === "all-types") {
48
+ if (filter.id === "all-types" || !filterKinds(filter).length) {
40
49
  return 0;
41
50
  }
42
51
  return getInBadgeNotifications(notifications).filter(
@@ -51,9 +60,13 @@ export function getScopedNotifications(notifications, tab) {
51
60
  }
52
61
  export function buildFiltersFromNotifications(notifications, tab) {
53
62
  const scoped = getScopedNotifications(notifications, tab);
54
- const typesPresent = new Set(scoped.map((notification) => notification.type));
63
+ const kindsPresent = new Set(
64
+ scoped.map(
65
+ (notification) => normalizeNotificationKind(notification.kind || notification.type)
66
+ )
67
+ );
55
68
  const categories = NOTIFICATION_FILTER_CATEGORIES.filter(
56
- (category) => category.types.some((type) => typesPresent.has(type))
69
+ (category) => category.kinds.some((kind) => kindsPresent.has(normalizeNotificationKind(kind)))
57
70
  );
58
71
  if (categories.length < 2) {
59
72
  return [];
@@ -76,7 +89,10 @@ export function filterHasContent(notifications, filter, tab) {
76
89
  export function getVisibleFilters(notifications, filters, tab) {
77
90
  return filters.filter((filter) => {
78
91
  if (filter.id === "other") {
79
- return notifications.some((notification) => notification.type === "unknown");
92
+ return notifications.some((notification) => {
93
+ const kind = normalizeNotificationKind(notification.kind || notification.type);
94
+ return kind === "unknown" || notification.type === "unknown";
95
+ });
80
96
  }
81
97
  return true;
82
98
  }).filter((filter) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apptegy/nuxt-navigation",
3
- "version": "0.1.114",
3
+ "version": "0.1.115",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -36,20 +36,19 @@
36
36
  "primevue": "^4.2.5"
37
37
  },
38
38
  "devDependencies": {
39
- "@nuxt/devtools": "^2.6.2",
40
- "@nuxt/eslint-config": "^1.6.0",
41
- "@nuxt/module-builder": "^1.0.1",
42
- "@nuxt/schema": "^3.17.0",
43
- "@nuxt/test-utils": "^3.17.0",
44
- "@types/node": "^22.0.0",
45
- "eslint": "^9.31.0",
46
- "nuxt": "^3.17.0",
39
+ "@nuxt/devtools": "catalog:",
40
+ "@nuxt/eslint-config": "catalog:",
41
+ "@nuxt/module-builder": "catalog:",
42
+ "@nuxt/schema": "catalog:",
43
+ "@nuxt/test-utils": "catalog:",
44
+ "@types/node": "catalog:",
45
+ "eslint": "catalog:",
46
+ "nuxt": "catalog:",
47
47
  "primevue": "^4.5.4",
48
- "sass": "^1.89.2",
49
- "typescript": "~5.8.0",
50
- "vitest": "^3.2.4",
51
- "vue": "^3.5.13",
52
- "vue-tsc": "^3.0.3"
53
- },
54
- "gitHead": "d57420f3128d73ad4ee181b2c437040434c0023c"
48
+ "sass": "catalog:",
49
+ "typescript": "catalog:",
50
+ "vitest": "catalog:",
51
+ "vue": "catalog:",
52
+ "vue-tsc": "catalog:"
53
+ }
55
54
  }