@memberjunction/ng-explorer-core 5.41.0 → 5.43.0

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 (29) hide show
  1. package/dist/generated/lazy-feature-config.d.ts +1 -1
  2. package/dist/generated/lazy-feature-config.d.ts.map +1 -1
  3. package/dist/generated/lazy-feature-config.js +5 -2
  4. package/dist/generated/lazy-feature-config.js.map +1 -1
  5. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts +7 -0
  6. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts.map +1 -1
  7. package/dist/lib/resource-wrappers/chat-collections-resource.component.js +42 -22
  8. package/dist/lib/resource-wrappers/chat-collections-resource.component.js.map +1 -1
  9. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts +179 -0
  10. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts.map +1 -0
  11. package/dist/lib/resource-wrappers/livekit-room-resource.component.js +1197 -0
  12. package/dist/lib/resource-wrappers/livekit-room-resource.component.js.map +1 -0
  13. package/dist/lib/shell/components/header/app-nav.component.js +7 -4
  14. package/dist/lib/shell/components/header/app-nav.component.js.map +1 -1
  15. package/dist/lib/shell/components/header/app-switcher.component.js +11 -6
  16. package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
  17. package/dist/lib/shell/shell.module.d.ts +1 -1
  18. package/dist/lib/shell/shell.module.d.ts.map +1 -1
  19. package/dist/lib/shell/shell.module.js +3 -1
  20. package/dist/lib/shell/shell.module.js.map +1 -1
  21. package/dist/lib/user-notifications/user-notifications.component.d.ts +6 -0
  22. package/dist/lib/user-notifications/user-notifications.component.d.ts.map +1 -1
  23. package/dist/lib/user-notifications/user-notifications.component.js +32 -1
  24. package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
  25. package/dist/module.d.ts +33 -31
  26. package/dist/module.d.ts.map +1 -1
  27. package/dist/module.js +11 -4
  28. package/dist/module.js.map +1 -1
  29. package/package.json +47 -46
@@ -0,0 +1 @@
1
+ {"version":3,"file":"livekit-room-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/livekit-room-resource.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, OnDestroy, AfterViewInit, ChangeDetectorRef, inject } from '@angular/core';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { RegisterClass, UUIDsEqual } from '@memberjunction/global';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport { RunView } from '@memberjunction/core';\nimport { GraphQLLiveKitClient, GraphQLDataProvider, RealtimeModelVoices, RealtimeVoiceOption } from '@memberjunction/graphql-dataprovider';\nimport { UserHoldsAuthorization, REALTIME_ADVANCED_SESSION_CONTROLS } from '@memberjunction/ng-conversations';\n\n/** A selectable target agent for the pre-join picker. */\ninterface TargetAgentChoice {\n ID: string;\n Name: string;\n Description: string | null;\n}\n\n/** A user shown in the \"Invite people\" search/selection. */\ninterface InviteeChoice {\n ID: string;\n Name: string;\n Email: string | null;\n}\n\n/**\n * LiveKit Room Resource — hosts the MJ-native LiveKit room (`mj-livekit-agent-room`) as an Explorer tab\n * (the **Meet** app's \"Live Room\").\n *\n * Flow: resolve the Realtime **co-agent** (voice front-end), then show a **pre-join picker** to choose\n * the **target** agent you're \"calling\" (the co-agent voices it via `invoke-target-agent` — without a\n * target it sits idle). \"Start call\" joins the room. An explicit `ResourceRecordID` skips the picker and\n * calls that agent directly.\n *\n * Registered via `@RegisterClass(BaseResourceComponent, 'LiveKitRoomResource')`.\n */\n@RegisterClass(BaseResourceComponent, 'LiveKitRoomResource')\n@Component({\n standalone: false,\n selector: 'mj-livekit-room-resource',\n template: `\n @switch (phase) {\n @case ('live') {\n <mj-livekit-agent-room\n class=\"mj-livekit-room-resource\"\n [Mode]=\"roomMode\"\n [RoomName]=\"joinRoomName\"\n [AgentID]=\"agentId\"\n [TargetAgentID]=\"targetAgentId\"\n [AgentName]=\"targetAgentName\"\n [AvailableAgents]=\"agents\"\n [RealtimeModelID]=\"selectedModelId\"\n [RealtimeVoice]=\"selectedVoice\"\n [CanPickModelVoice]=\"canPickModelVoice\"\n [AvailableModels]=\"realtimeModels\"\n [Provider]=\"ProviderToUse\"\n [ShowAgentState]=\"true\"\n [ShowWhiteboard]=\"true\"\n [EnableLayoutSwitcher]=\"true\"\n [EnablePinning]=\"true\"\n (Connected)=\"NotifyLoadComplete()\"\n (Disconnected)=\"onRoomLeft()\"\n (ErrorOccurred)=\"NotifyLoadComplete()\"\n (InvitePeopleRequested)=\"openInvite($event)\"\n ></mj-livekit-agent-room>\n }\n @case ('picking') {\n <div class=\"mj-lk-prejoin\">\n <div class=\"mj-lk-prejoin__card\">\n <button type=\"button\" class=\"mj-lk-back\" (click)=\"backToLanding()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <div class=\"mj-lk-prejoin__icon\"><i class=\"fa-solid fa-video\"></i></div>\n <h2 class=\"mj-lk-prejoin__title\">Start a call</h2>\n <p class=\"mj-lk-prejoin__subtitle\">\n Choose the agent you want to talk to. You can add more agents (and invite people) once\n you're in the room.\n </p>\n <label class=\"mj-lk-prejoin__label\" for=\"mj-lk-target\">Agent</label>\n <select id=\"mj-lk-target\" class=\"mj-input mj-lk-prejoin__select\" (change)=\"onTargetChange($event)\">\n @for (a of agents; track a.ID) {\n <option [value]=\"a.ID\" [selected]=\"UUIDsEqual(a.ID, selectedTargetId)\">{{ a.Name }}</option>\n }\n </select>\n @if (selectedDescription) {\n <p class=\"mj-lk-prejoin__desc\">{{ selectedDescription }}</p>\n }\n @if (canPickModelVoice && realtimeModels.length) {\n <label class=\"mj-lk-prejoin__label\" for=\"mj-lk-model\">Model <span class=\"mj-lk-prejoin__dev\">dev</span></label>\n <select id=\"mj-lk-model\" class=\"mj-input mj-lk-prejoin__select\" (change)=\"onModelChange($event)\">\n <option value=\"\">Default model</option>\n @for (m of realtimeModels; track m.ModelID) {\n <option [value]=\"m.ModelID\" [selected]=\"UUIDsEqual(m.ModelID, selectedModelId)\">{{ m.ModelName }}</option>\n }\n </select>\n @if (selectedModelVoices.length) {\n <label class=\"mj-lk-prejoin__label\" for=\"mj-lk-voice\">Voice <span class=\"mj-lk-prejoin__dev\">dev</span></label>\n <select id=\"mj-lk-voice\" class=\"mj-input mj-lk-prejoin__select\" (change)=\"onVoiceChange($event)\">\n <option value=\"\">Default voice</option>\n @for (v of selectedModelVoices; track v.ID) {\n <option [value]=\"v.ID\" [selected]=\"v.ID === selectedVoice\">{{ v.Name }}</option>\n }\n </select>\n }\n }\n <button type=\"button\" class=\"mj-lk-prejoin__start\" [disabled]=\"!selectedTargetId\" (click)=\"startCall()\">\n <i class=\"fa-solid fa-phone\"></i> Start call\n </button>\n </div>\n </div>\n }\n @case ('landing') {\n <div class=\"mj-lk-prejoin\">\n <div class=\"mj-lk-prejoin__card mj-lk-prejoin__card--wide\">\n <div class=\"mj-lk-prejoin__icon\"><i class=\"fa-solid fa-video\"></i></div>\n <h2 class=\"mj-lk-prejoin__title\">Meet</h2>\n <p class=\"mj-lk-prejoin__subtitle\">Start a live room with agents and people, join one that's in progress, or review a past meeting.</p>\n <div class=\"mj-lk-cards\">\n <button type=\"button\" class=\"mj-lk-card\" (click)=\"startNewRoom()\">\n <i class=\"fa-solid fa-circle-plus\"></i>\n <span class=\"mj-lk-card__t\">New room</span>\n <span class=\"mj-lk-card__d\">Pick an agent and start a fresh call.</span>\n </button>\n <button type=\"button\" class=\"mj-lk-card\" (click)=\"openExisting()\">\n <i class=\"fa-solid fa-door-open\"></i>\n <span class=\"mj-lk-card__t\">Join existing</span>\n <span class=\"mj-lk-card__d\">Hop into a room that's live right now.</span>\n </button>\n <button type=\"button\" class=\"mj-lk-card\" (click)=\"openHistory()\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n <span class=\"mj-lk-card__t\">History</span>\n <span class=\"mj-lk-card__d\">Read transcripts of past meetings.</span>\n </button>\n </div>\n </div>\n </div>\n }\n @case ('existing') {\n <div class=\"mj-lk-prejoin\">\n <div class=\"mj-lk-prejoin__card mj-lk-prejoin__card--wide\">\n <button type=\"button\" class=\"mj-lk-back\" (click)=\"backToLanding()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h2 class=\"mj-lk-prejoin__title\">Join a room</h2>\n @if (loadingActive) {\n <div class=\"mj-lk-listmsg\"><i class=\"fa-solid fa-spinner fa-spin\"></i> Finding active rooms…</div>\n } @else if (activeRooms.length) {\n <div class=\"mj-lk-list\">\n @for (r of activeRooms; track r.RoomName) {\n <button type=\"button\" class=\"mj-lk-list__row\" (click)=\"joinExistingRoom(r.RoomName)\">\n <span class=\"mj-lk-list__name\"><i class=\"fa-solid fa-circle mj-lk-livedot\"></i> {{ r.Label }}</span>\n <span class=\"mj-lk-list__meta\">{{ r.AgentCount }} agent{{ r.AgentCount === 1 ? '' : 's' }} · Join</span>\n </button>\n }\n </div>\n } @else {\n <p class=\"mj-lk-prejoin__desc\">No rooms are active right now.</p>\n }\n <label class=\"mj-lk-prejoin__label\" for=\"mj-lk-room\">Or join by name</label>\n <div class=\"mj-lk-joinrow\">\n <input id=\"mj-lk-room\" class=\"mj-input mj-lk-joinrow__input\" type=\"text\" placeholder=\"Room name…\" [value]=\"manualRoomName\"\n (input)=\"manualRoomName = $any($event.target).value\" (keydown.enter)=\"joinExistingRoom(manualRoomName)\" />\n <button type=\"button\" class=\"mj-lk-prejoin__start mj-lk-joinrow__btn\" [disabled]=\"!manualRoomName.trim()\" (click)=\"joinExistingRoom(manualRoomName)\">Join</button>\n </div>\n </div>\n </div>\n }\n @case ('history') {\n <div class=\"mj-lk-prejoin\">\n <div class=\"mj-lk-prejoin__card mj-lk-prejoin__card--wide\">\n @if (!openHistoryRoom) {\n <button type=\"button\" class=\"mj-lk-back\" (click)=\"backToLanding()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h2 class=\"mj-lk-prejoin__title\">Past meetings</h2>\n @if (loadingHistory) {\n <div class=\"mj-lk-listmsg\"><i class=\"fa-solid fa-spinner fa-spin\"></i> Loading…</div>\n } @else if (historyRooms.length) {\n <div class=\"mj-lk-list\">\n @for (h of historyRooms; track h.ConversationID) {\n <button type=\"button\" class=\"mj-lk-list__row\" (click)=\"openTranscript(h)\">\n <span class=\"mj-lk-list__name\"><i class=\"fa-solid fa-comments\"></i> {{ h.Name }}</span>\n <span class=\"mj-lk-list__meta\">{{ formatTime(h.At) }}</span>\n </button>\n }\n </div>\n } @else {\n <p class=\"mj-lk-prejoin__desc\">No past meetings yet.</p>\n }\n } @else {\n <button type=\"button\" class=\"mj-lk-back\" (click)=\"closeTranscript()\"><i class=\"fa-solid fa-arrow-left\"></i> All meetings</button>\n <h2 class=\"mj-lk-prejoin__title\">{{ openHistoryRoom.Name }}</h2>\n @if (loadingTranscript) {\n <div class=\"mj-lk-listmsg\"><i class=\"fa-solid fa-spinner fa-spin\"></i> Loading transcript…</div>\n } @else if (historyTranscript.length) {\n <div class=\"mj-lk-transcript\">\n @for (line of historyTranscript; track $index) {\n <div class=\"mj-lk-tline\" [class.mj-lk-tline--agent]=\"line.Kind === 'agent'\" [class.mj-lk-tline--error]=\"line.Kind === 'error'\">\n <span class=\"mj-lk-tline__who\">{{ line.Speaker }}</span>\n <span class=\"mj-lk-tline__msg\">{{ line.Message }}</span>\n </div>\n }\n </div>\n } @else {\n <p class=\"mj-lk-prejoin__desc\">No transcript was captured for this meeting.</p>\n }\n }\n </div>\n </div>\n }\n @case ('error') {\n <div class=\"mj-livekit-room-resource__message\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n <span>{{ resolveError }}</span>\n </div>\n }\n @default {\n <div class=\"mj-livekit-room-resource__message\">Preparing room…</div>\n }\n }\n\n @if (showInvite) {\n <mj-dialog [Visible]=\"showInvite\" (Close)=\"showInvite = false\" Title=\"Invite people\" Size=\"auto\" [Width]=\"440\">\n <div class=\"mj-lk-invite\">\n <p class=\"mj-lk-invite__hint\">They'll get an in-app notification (and an email if Comms is set up) to join this room.</p>\n <div class=\"mj-lk-invite__search\">\n <input class=\"mj-input\" type=\"text\" [value]=\"userSearch\" placeholder=\"Search people by name or email…\"\n (input)=\"userSearch = $any($event.target).value\" (keydown.enter)=\"searchUsers()\" />\n <button type=\"button\" class=\"mj-lk-invite__searchbtn\" [disabled]=\"searching\" (click)=\"searchUsers()\">\n <i class=\"fa-solid\" [class.fa-magnifying-glass]=\"!searching\" [class.fa-spinner]=\"searching\" [class.fa-spin]=\"searching\"></i>\n </button>\n </div>\n\n @if (selectedInvitees.length) {\n <div class=\"mj-lk-invite__chips\">\n @for (u of selectedInvitees; track u.ID) {\n <span class=\"mj-lk-invite__chip\">\n {{ u.Name }}\n <button type=\"button\" class=\"mj-lk-invite__chipx\" (click)=\"removeInvitee(u.ID)\"><i class=\"fa-solid fa-xmark\"></i></button>\n </span>\n }\n </div>\n }\n\n @if (userResults.length) {\n <div class=\"mj-lk-invite__results\">\n @for (u of userResults; track u.ID) {\n <button type=\"button\" class=\"mj-lk-invite__result\" (click)=\"addInvitee(u)\">\n <span class=\"mj-lk-invite__rname\">{{ u.Name }}</span>\n @if (u.Email) { <span class=\"mj-lk-invite__remail\">{{ u.Email }}</span> }\n </button>\n }\n </div>\n } @else if (searched && !searching) {\n <div class=\"mj-lk-invite__empty\">No matching people.</div>\n }\n\n @if (inviteMessage) {\n <div class=\"mj-lk-invite__msg\">{{ inviteMessage }}</div>\n }\n\n <div class=\"mj-lk-invite__actions\">\n <button type=\"button\" class=\"mj-lk-invite__send\" [disabled]=\"!selectedInvitees.length || inviting\" (click)=\"sendInvites()\">\n <i class=\"fa-solid\" [class.fa-paper-plane]=\"!inviting\" [class.fa-spinner]=\"inviting\" [class.fa-spin]=\"inviting\"></i>\n Invite {{ selectedInvitees.length || '' }}\n </button>\n <button type=\"button\" class=\"mj-lk-invite__cancel\" (click)=\"showInvite = false\">Cancel</button>\n </div>\n </div>\n </mj-dialog>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n }\n .mj-livekit-room-resource {\n display: block;\n height: 100%;\n }\n .mj-livekit-room-resource__message {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n height: 100%;\n padding: 1.5rem;\n text-align: center;\n color: var(--mj-text-secondary);\n }\n .mj-livekit-room-resource__message i {\n color: var(--mj-status-warning);\n }\n .mj-lk-prejoin {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 1.5rem;\n background: var(--mj-bg-page);\n }\n .mj-lk-prejoin__card {\n width: 100%;\n max-width: 440px;\n padding: 2rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-md, 0 4px 16px rgba(0, 0, 0, 0.12));\n text-align: center;\n }\n .mj-lk-prejoin__icon {\n font-size: 1.75rem;\n color: var(--mj-brand-primary);\n margin-bottom: 0.75rem;\n }\n .mj-lk-prejoin__title {\n margin: 0 0 0.5rem;\n color: var(--mj-text-primary);\n font-size: 1.25rem;\n }\n .mj-lk-prejoin__subtitle {\n margin: 0 0 1.5rem;\n color: var(--mj-text-secondary);\n font-size: 0.875rem;\n line-height: 1.4;\n }\n .mj-lk-prejoin__label {\n display: block;\n text-align: left;\n margin-bottom: 0.35rem;\n color: var(--mj-text-secondary);\n font-size: 0.8125rem;\n font-weight: 600;\n }\n .mj-lk-prejoin__dev {\n margin-left: 0.35rem;\n padding: 0 0.35rem;\n border-radius: 999px;\n font-size: 0.625rem;\n font-weight: 700;\n text-transform: uppercase;\n color: var(--mj-text-inverse);\n background: var(--mj-brand-primary);\n vertical-align: middle;\n }\n .mj-lk-prejoin__select {\n width: 100%;\n margin-bottom: 0.75rem;\n }\n .mj-lk-prejoin__desc {\n text-align: left;\n margin: 0 0 1.25rem;\n color: var(--mj-text-muted);\n font-size: 0.8125rem;\n line-height: 1.4;\n }\n .mj-lk-prejoin__start {\n width: 100%;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.625rem 1rem;\n border: none;\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.9375rem;\n font-weight: 600;\n cursor: pointer;\n }\n .mj-lk-prejoin__start:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n }\n .mj-lk-prejoin__start:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n .mj-lk-prejoin__card--wide {\n max-width: 560px;\n text-align: left;\n }\n .mj-lk-back {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 0.75rem;\n padding: 4px 8px;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-size: 0.8125rem;\n }\n .mj-lk-back:hover { color: var(--mj-text-primary); }\n .mj-lk-cards {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 0.75rem;\n margin-top: 1.25rem;\n }\n .mj-lk-card {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 1.25rem 0.75rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n background: var(--mj-bg-surface-card);\n cursor: pointer;\n text-align: center;\n }\n .mj-lk-card:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-hover, var(--mj-bg-surface-card));\n }\n .mj-lk-card > i { font-size: 1.4rem; color: var(--mj-brand-primary); }\n .mj-lk-card__t { font-weight: 600; color: var(--mj-text-primary); font-size: 0.9rem; }\n .mj-lk-card__d { color: var(--mj-text-muted); font-size: 0.78rem; line-height: 1.3; }\n .mj-lk-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 0.5rem 0 1rem;\n max-height: 320px;\n overflow-y: auto;\n }\n .mj-lk-list__row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 10px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface-card);\n cursor: pointer;\n text-align: left;\n }\n .mj-lk-list__row:hover { border-color: var(--mj-brand-primary); background: var(--mj-bg-surface-hover, var(--mj-bg-surface-card)); }\n .mj-lk-list__name { color: var(--mj-text-primary); font-size: 0.875rem; display: inline-flex; align-items: center; gap: 8px; }\n .mj-lk-list__meta { color: var(--mj-text-muted); font-size: 0.78rem; white-space: nowrap; }\n .mj-lk-livedot { color: var(--mj-status-success); font-size: 0.5rem; }\n .mj-lk-listmsg { padding: 1rem; color: var(--mj-text-secondary); text-align: center; }\n .mj-lk-joinrow { display: flex; gap: 8px; }\n .mj-lk-joinrow__input { flex: 1; }\n .mj-lk-joinrow__btn { white-space: nowrap; }\n .mj-lk-transcript {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 0.5rem;\n max-height: 60vh;\n overflow-y: auto;\n }\n .mj-lk-tline {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--mj-bg-surface-card);\n }\n .mj-lk-tline--agent { background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)); }\n .mj-lk-tline--error { background: var(--mj-status-error-bg, color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface))); }\n .mj-lk-tline__who { font-size: 0.72rem; font-weight: 600; color: var(--mj-text-secondary); }\n .mj-lk-tline__msg { font-size: 0.875rem; color: var(--mj-text-primary); }\n .mj-lk-invite {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n min-width: 360px;\n padding: 0.25rem;\n }\n .mj-lk-invite__hint {\n margin: 0;\n color: var(--mj-text-muted);\n font-size: 0.8125rem;\n }\n .mj-lk-invite__search {\n display: flex;\n gap: 0.5rem;\n }\n .mj-lk-invite__search .mj-input {\n flex: 1;\n }\n .mj-lk-invite__searchbtn,\n .mj-lk-invite__send,\n .mj-lk-invite__cancel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.4rem;\n padding: 0.5rem 0.85rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n cursor: pointer;\n font-size: 0.875rem;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n }\n .mj-lk-invite__send {\n border: none;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n .mj-lk-invite__send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n .mj-lk-invite__chips {\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem;\n }\n .mj-lk-invite__chip {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.2rem 0.5rem;\n border-radius: 999px;\n font-size: 0.8125rem;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n }\n .mj-lk-invite__chipx {\n border: none;\n background: transparent;\n cursor: pointer;\n color: var(--mj-text-muted);\n padding: 0;\n }\n .mj-lk-invite__results {\n max-height: 220px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n }\n .mj-lk-invite__result {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0.1rem;\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: none;\n border-bottom: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n background: transparent;\n cursor: pointer;\n text-align: left;\n }\n .mj-lk-invite__result:hover {\n background: var(--mj-bg-surface-hover);\n }\n .mj-lk-invite__rname {\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n }\n .mj-lk-invite__remail {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n }\n .mj-lk-invite__empty {\n color: var(--mj-text-muted);\n font-size: 0.8125rem;\n padding: 0.25rem;\n }\n .mj-lk-invite__msg {\n color: var(--mj-text-secondary);\n font-size: 0.8125rem;\n }\n .mj-lk-invite__actions {\n display: flex;\n gap: 0.5rem;\n margin-top: 0.25rem;\n }\n `,\n ],\n})\nexport class LiveKitRoomResource extends BaseResourceComponent implements OnInit, OnDestroy, AfterViewInit {\n /** Render phase: spinner while resolving → landing (new/existing/history) → picker/existing/history → live room. */\n public phase: 'loading' | 'landing' | 'picking' | 'existing' | 'history' | 'live' | 'error' = 'loading';\n\n // ── \"Join existing\" state ─────────────────────────────────────────────────────────\n /** Currently-active rooms (a distinct `ExternalConnectionID` with ≥1 connected agent bridge). */\n public activeRooms: { RoomName: string; Label: string; AgentCount: number }[] = [];\n /** True while loading the active-rooms list. */\n public loadingActive = false;\n /** Free-text room name to join directly (the \"join by name/code\" path). */\n public manualRoomName = '';\n\n // ── \"History\" (past meetings) state ───────────────────────────────────────────────\n /** Past meeting rooms (the `MJ: Conversations` of Type='Meeting Room' the bridge recorded). */\n public historyRooms: { ConversationID: string; Name: string; At: Date }[] = [];\n /** True while loading the history list. */\n public loadingHistory = false;\n /** The history room whose transcript is open (drill-in), or null for the list. */\n public openHistoryRoom: { ConversationID: string; Name: string } | null = null;\n /** The opened room's transcript lines. */\n public historyTranscript: { Kind: 'agent' | 'human' | 'error'; Speaker: string; Message: string }[] = [];\n /** True while loading a room's transcript. */\n public loadingTranscript = false;\n\n /** `'agent'` to start/voice an agent (the default), or `'join'` when opened from an invite link. */\n public roomMode: 'agent' | 'join' = 'agent';\n\n /** The room to JOIN when opened via an invite link (`?room=…`); null in agent mode. */\n public joinRoomName: string | null = null;\n\n /** The Realtime co-agent (voice front-end) id — the resolved default Realtime-type agent. */\n public agentId: string | null = null;\n\n /** The TARGET agent the co-agent voices (the one being \"called\"); the bot is named after it. */\n public targetAgentId: string | null = null;\n\n /** The target agent's display name — used as the bot name + addressing word (\"Sage, …\"). */\n public targetAgentName: string | null = null;\n\n /** The agents offered in the pre-join picker (active, excluding the Realtime co-agent itself). */\n public agents: TargetAgentChoice[] = [];\n\n /** The currently-selected target in the picker (defaults to \"Sage\" when present). */\n public selectedTargetId: string | null = null;\n\n // ── Dev model/voice override (gated by the `Realtime: Advanced Session Controls` authorization) ──────\n /** Whether the current user may override the realtime model/voice (drives the dev pickers). */\n public canPickModelVoice = false;\n /** Active Realtime models + their voices (loaded once when the user can override). */\n public realtimeModels: RealtimeModelVoices[] = [];\n /** The MODEL override chosen for the FIRST agent in the pre-join picker (null = default). */\n public selectedModelId: string | null = null;\n /** The VOICE override chosen for the FIRST agent in the pre-join picker (null = default). */\n public selectedVoice: string | null = null;\n\n /** Voices for the model chosen in the pre-join picker. */\n public get selectedModelVoices(): RealtimeVoiceOption[] {\n return this.realtimeModels.find((m) => UUIDsEqual(m.ModelID, this.selectedModelId))?.Voices ?? [];\n }\n\n /** Records the pre-join MODEL choice; clears the voice so it can't outlive a model switch. */\n public onModelChange(event: Event): void {\n this.selectedModelId = (event.target as HTMLSelectElement).value || null;\n this.selectedVoice = null;\n }\n\n /** Records the pre-join VOICE choice. */\n public onVoiceChange(event: Event): void {\n this.selectedVoice = (event.target as HTMLSelectElement).value || null;\n }\n\n /** Exposed for template use — platform-safe UUID equality (SQL upper vs PG lower). */\n public UUIDsEqual = UUIDsEqual;\n\n /** Set when no co-agent could be resolved — shown instead of the room. */\n public resolveError: string | null = null;\n\n // ── \"Invite people\" dialog state ─────────────────────────────────────────────────\n /** Whether the invite-people dialog is open. */\n public showInvite = false;\n /** The room the invite targets (the live room name). */\n public inviteRoomName: string | null = null;\n /** Current search text in the people search box. */\n public userSearch = '';\n /** People matching the last search (minus already-selected + the current user). */\n public userResults: InviteeChoice[] = [];\n /** People chosen to invite. */\n public selectedInvitees: InviteeChoice[] = [];\n /** True while a people search is running. */\n public searching = false;\n /** True once a search has run (drives the \"no matches\" empty state). */\n public searched = false;\n /** True while invites are being sent. */\n public inviting = false;\n /** Result/status message under the dialog. */\n public inviteMessage: string | null = null;\n\n private readonly cdr = inject(ChangeDetectorRef);\n\n /** Description of the selected target, shown under the picker. */\n public get selectedDescription(): string | null {\n return this.agents.find((a) => UUIDsEqual(a.ID, this.selectedTargetId))?.Description ?? null;\n }\n\n override ngOnInit(): void {\n super.ngOnInit();\n this.resolve();\n }\n\n /**\n * Resolves the co-agent + target choices. Synchronous when the AI engine is already loaded (the common\n * case — it bootstraps at startup) so the first render is settled (avoids NG0100 from an async branch\n * flip under the app's frequent change-detection); a cold engine takes the async path and flushes.\n */\n private resolve(): void {\n // Invite link (?room=…): join that existing room directly — no agent resolution / picker needed.\n const invitedRoom = this.GetQueryParams()?.['room']?.trim();\n if (invitedRoom) {\n this.roomMode = 'join';\n this.joinRoomName = invitedRoom;\n this.phase = 'live';\n this.NotifyLoadComplete();\n return;\n }\n\n if (AIEngineBase.Instance.Loaded) {\n this.applyResolution();\n return;\n }\n void AIEngineBase.Instance.Config(false, undefined, this.ProviderToUse).then(() => {\n this.applyResolution();\n this.cdr.detectChanges();\n });\n }\n\n /** Resolves the co-agent, builds the target list, and chooses the next phase (live for an explicit record, else picker). */\n private applyResolution(): void {\n const realtimeType = AIEngineBase.Instance.AgentTypes.find((t) => t.Name?.trim().toLowerCase() === 'realtime');\n const coAgent = realtimeType\n ? AIEngineBase.Instance.Agents.find((a) => a.Status === 'Active' && UUIDsEqual(a.TypeID, realtimeType.ID))\n : undefined;\n\n if (!coAgent) {\n this.resolveError =\n 'No active Realtime agent is configured, so the Live Room has no voice to bring in. ' +\n 'Create a Realtime-type AI Agent (e.g. \"Realtime Co-Agent\") and try again.';\n this.phase = 'error';\n this.NotifyLoadComplete();\n return;\n }\n this.agentId = coAgent.ID;\n\n // Dev model/voice override: gate on the `Realtime: Advanced Session Controls` authorization and, when\n // held, load the active models + their voices for the pickers (here and the in-room add-agent control).\n this.canPickModelVoice = UserHoldsAuthorization(\n this.ProviderToUse?.CurrentUser, REALTIME_ADVANCED_SESSION_CONTROLS, this.ProviderToUse,\n );\n if (this.canPickModelVoice) {\n void this.loadRealtimeModels();\n }\n\n // Target candidates: every active agent EXCEPT the Realtime co-agents (they voice a target, not themselves).\n this.agents = AIEngineBase.Instance.Agents\n .filter((a) => a.Status === 'Active' && (!realtimeType || !UUIDsEqual(a.TypeID, realtimeType.ID)))\n .map((a) => ({ ID: a.ID, Name: a.Name ?? '(unnamed)', Description: a.Description ?? null }))\n .sort((a, b) => a.Name.localeCompare(b.Name));\n\n // An explicit record id (deep link to a specific agent) skips the picker and calls it directly.\n const explicit = this.Data?.ResourceRecordID ? String(this.Data.ResourceRecordID) : null;\n if (explicit) {\n this.selectedTargetId = explicit;\n this.startCall();\n return;\n }\n\n // Default the picker to the general assistant \"Sage\" when present, else the first candidate.\n this.selectedTargetId =\n this.agents.find((a) => a.Name.trim().toLowerCase() === 'sage')?.ID ?? this.agents[0]?.ID ?? null;\n // Land on the Meet home (start new / join existing / history) rather than jumping straight into the picker.\n this.phase = 'landing';\n this.NotifyLoadComplete();\n }\n\n // ── Meet landing navigation ─────────────────────────────────────────────────────────\n\n /**\n * The room disconnected (the user clicked **Leave** or **End meeting**, or the room dropped). Return to the\n * Meet landing so they can start a new room, join another, or review history — rather than being stranded\n * on a dead room. Resets the join state so the next \"New room\" / \"Join existing\" starts clean.\n */\n public onRoomLeft(): void {\n this.roomMode = 'agent';\n this.joinRoomName = null;\n this.phase = 'landing';\n this.cdr.detectChanges();\n }\n\n /** Go to the \"start a new room\" picker. */\n public startNewRoom(): void {\n this.phase = 'picking';\n }\n\n /** Open the \"join existing room\" view and load the active rooms. */\n public openExisting(): void {\n this.phase = 'existing';\n void this.loadActiveRooms();\n }\n\n /** Open the \"past meetings\" history view and load the list. */\n public openHistory(): void {\n this.phase = 'history';\n this.openHistoryRoom = null;\n void this.loadHistory();\n }\n\n /** Back to the Meet home. */\n public backToLanding(): void {\n this.phase = 'landing';\n }\n\n /** Joins an existing room by its LiveKit room name (the bridge `ExternalConnectionID`). */\n public joinExistingRoom(roomName: string): void {\n const name = roomName.trim();\n if (!name) {\n return;\n }\n this.roomMode = 'join';\n this.joinRoomName = name;\n this.phase = 'live';\n }\n\n /** Loads currently-active rooms: distinct rooms that have a Connected/Connecting agent bridge. */\n private async loadActiveRooms(): Promise<void> {\n this.loadingActive = true;\n this.activeRooms = [];\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const res = await rv.RunView<{ ExternalConnectionID: string; Agent: string }>(\n {\n EntityName: 'MJ: AI Agent Session Bridges',\n ExtraFilter: `Status IN ('Connected','Connecting') AND ExternalConnectionID IS NOT NULL`,\n Fields: ['ExternalConnectionID', 'Agent'],\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 500,\n ResultType: 'simple',\n },\n this.ProviderToUse.CurrentUser,\n );\n if (res.Success) {\n const byRoom = new Map<string, { count: number; agents: Set<string> }>();\n for (const r of res.Results) {\n const room = String(r['ExternalConnectionID'] ?? '');\n if (!room) {\n continue;\n }\n const entry = byRoom.get(room) ?? { count: 0, agents: new Set<string>() };\n entry.count += 1;\n if (r['Agent']) {\n entry.agents.add(String(r['Agent']));\n }\n byRoom.set(room, entry);\n }\n this.activeRooms = [...byRoom.entries()].map(([room, info]) => ({\n RoomName: room,\n Label: info.agents.size ? [...info.agents].join(', ') : room,\n AgentCount: info.count,\n }));\n }\n } finally {\n this.loadingActive = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Loads past meetings: the `Meeting Room` conversations the bridge recorded, newest first. */\n private async loadHistory(): Promise<void> {\n this.loadingHistory = true;\n this.historyRooms = [];\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const res = await rv.RunView<{ ID: string; Name: string; __mj_UpdatedAt: string }>(\n {\n EntityName: 'MJ: Conversations',\n ExtraFilter: `Type='Meeting Room'`,\n Fields: ['ID', 'Name', '__mj_UpdatedAt'],\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 200,\n ResultType: 'simple',\n },\n this.ProviderToUse.CurrentUser,\n );\n if (res.Success) {\n this.historyRooms = res.Results.map((r) => ({\n ConversationID: String(r['ID']),\n Name: String(r['Name'] ?? 'Meeting'),\n At: new Date(String(r['__mj_UpdatedAt'] ?? '')),\n }));\n }\n } finally {\n this.loadingHistory = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Opens a past meeting's transcript (drill-in). */\n public async openTranscript(room: { ConversationID: string; Name: string }): Promise<void> {\n this.openHistoryRoom = room;\n this.historyTranscript = [];\n this.loadingTranscript = true;\n this.cdr.detectChanges();\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const res = await rv.RunView<{ Role: string; Message: string; AgentID: string; Error: string }>(\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: `ConversationID='${room.ConversationID.replace(/'/g, \"''\")}'`,\n Fields: ['Role', 'Message', 'AgentID', 'Error', '__mj_CreatedAt'],\n OrderBy: '__mj_CreatedAt ASC',\n MaxRows: 5000,\n ResultType: 'simple',\n },\n this.ProviderToUse.CurrentUser,\n );\n if (res.Success) {\n this.historyTranscript = res.Results.map((d) => {\n const role = String(d['Role'] ?? 'User');\n if (role === 'Error') {\n return { Kind: 'error' as const, Speaker: 'Error', Message: String(d['Error'] ?? d['Message'] ?? '') };\n }\n if (role === 'AI') {\n const agentId = String(d['AgentID'] ?? '').toLowerCase();\n const name = AIEngineBase.Instance.Agents.find((a) => a.ID.toLowerCase() === agentId)?.Name ?? 'Agent';\n return { Kind: 'agent' as const, Speaker: name, Message: String(d['Message'] ?? '') };\n }\n return { Kind: 'human' as const, Speaker: 'Participant', Message: String(d['Message'] ?? '') };\n });\n }\n } finally {\n this.loadingTranscript = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Back to the history list from a transcript drill-in. */\n public closeTranscript(): void {\n this.openHistoryRoom = null;\n }\n\n /** Formats a meeting timestamp for the history list (avoids a `date` pipe / CommonModule dependency). */\n public formatTime(d: Date): string {\n return d && !isNaN(d.getTime()) ? d.toLocaleString() : '';\n }\n\n /** Picker selection handler (native select; avoids a FormsModule dependency). */\n public onTargetChange(event: Event): void {\n this.selectedTargetId = (event.target as HTMLSelectElement).value || null;\n }\n\n /** Loads active Realtime models + their voices for the dev pickers (best-effort; empty on failure). */\n private async loadRealtimeModels(): Promise<void> {\n try {\n const client = new GraphQLLiveKitClient(this.ProviderToUse as unknown as GraphQLDataProvider);\n this.realtimeModels = await client.GetRealtimeModelVoices();\n } catch {\n this.realtimeModels = [];\n }\n this.cdr.markForCheck();\n }\n\n /** Commits the chosen target and switches to the live room. */\n public startCall(): void {\n if (!this.selectedTargetId) {\n return;\n }\n this.targetAgentId = this.selectedTargetId;\n this.targetAgentName = this.agents.find((a) => UUIDsEqual(a.ID, this.selectedTargetId))?.Name ?? null;\n this.phase = 'live';\n }\n\n /** Opens the invite-people dialog for the given room. */\n public openInvite(roomName: string): void {\n this.inviteRoomName = roomName || null;\n this.userSearch = '';\n this.userResults = [];\n this.selectedInvitees = [];\n this.searched = false;\n this.inviteMessage = null;\n this.showInvite = true;\n }\n\n /** Searches `MJ: Users` by name/email, excluding the current user + already-selected people. */\n public async searchUsers(): Promise<void> {\n const term = this.userSearch.trim();\n if (!term) {\n return;\n }\n this.searching = true;\n this.cdr.markForCheck();\n try {\n const p = this.ProviderToUse;\n const escaped = term.replace(/'/g, \"''\");\n const excluded = [p.CurrentUser?.ID, ...this.selectedInvitees.map((u) => u.ID)].filter(Boolean) as string[];\n const excludeFilter = excluded.length ? ` AND ID NOT IN (${excluded.map((id) => `'${id}'`).join(',')})` : '';\n const result = await RunView.FromMetadataProvider(p).RunView<{ ID: string; Name: string; Email: string }>({\n EntityName: 'Users',\n ExtraFilter: `IsActive = 1 AND (Name LIKE '%${escaped}%' OR Email LIKE '%${escaped}%')${excludeFilter}`,\n OrderBy: 'Name',\n Fields: ['ID', 'Name', 'Email'],\n MaxRows: 20,\n ResultType: 'simple',\n });\n this.userResults = (result.Success ? result.Results : []).map((u) => ({\n ID: u.ID,\n Name: u.Name || u.Email || '(unnamed)',\n Email: u.Email ?? null,\n }));\n } finally {\n this.searching = false;\n this.searched = true;\n this.cdr.markForCheck();\n }\n }\n\n /** Adds a person to the invite list (and out of the results). */\n public addInvitee(user: InviteeChoice): void {\n if (!this.selectedInvitees.some((u) => UUIDsEqual(u.ID, user.ID))) {\n this.selectedInvitees = [...this.selectedInvitees, user];\n }\n this.userResults = this.userResults.filter((u) => !UUIDsEqual(u.ID, user.ID));\n }\n\n /** Removes a person from the invite list. */\n public removeInvitee(userId: string): void {\n this.selectedInvitees = this.selectedInvitees.filter((u) => !UUIDsEqual(u.ID, userId));\n }\n\n /** Sends the invites — the server notifies each person (in-app + Comms when configured). */\n public async sendInvites(): Promise<void> {\n if (!this.selectedInvitees.length || !this.inviteRoomName || this.inviting) {\n return;\n }\n this.inviting = true;\n this.inviteMessage = null;\n this.cdr.markForCheck();\n try {\n const client = new GraphQLLiveKitClient(this.ProviderToUse as unknown as GraphQLDataProvider);\n const ok = await client.InviteUsers(this.inviteRoomName, this.selectedInvitees.map((u) => u.ID));\n if (ok) {\n this.inviteMessage = `Invited ${this.selectedInvitees.length} ${this.selectedInvitees.length === 1 ? 'person' : 'people'}.`;\n this.selectedInvitees = [];\n setTimeout(() => {\n this.showInvite = false;\n this.cdr.markForCheck();\n }, 900);\n } else {\n this.inviteMessage = 'Could not send the invites. Please try again.';\n }\n } catch (err) {\n this.inviteMessage = err instanceof Error ? err.message : String(err);\n } finally {\n this.inviting = false;\n this.cdr.markForCheck();\n }\n }\n\n ngAfterViewInit(): void {\n // Clear the shell loading screen even if resolution is slow — the picker/room/message shows its own\n // state, so the loader must not hang on direct URL navigation.\n this.NotifyLoadComplete();\n }\n\n override ngOnDestroy(): void {\n super.ngOnDestroy();\n }\n\n async GetResourceDisplayName(_data: ResourceData): Promise<string> {\n return 'Live Room';\n }\n\n async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-video';\n }\n}\n"]}
@@ -3,6 +3,7 @@ import { Subject, takeUntil } from 'rxjs';
3
3
  import * as i0 from "@angular/core";
4
4
  import * as i1 from "@memberjunction/ng-base-application";
5
5
  import * as i2 from "@memberjunction/ng-shared";
6
+ import * as i3 from "@memberjunction/ng-ui-components";
6
7
  function AppNavComponent_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
7
8
  i0.ɵɵelement(0, "i");
8
9
  } if (rf & 2) {
@@ -40,6 +41,8 @@ function AppNavComponent_For_2_Template(rf, ctx) { if (rf & 1) {
40
41
  const item_r2 = ctx.$implicit;
41
42
  const ctx_r2 = i0.ɵɵnextContext();
42
43
  i0.ɵɵclassProp("active", ctx_r2.isActive(item_r2))("dynamic", ctx_r2.isDynamic(item_r2))("no-icon", !item_r2.Icon);
44
+ i0.ɵɵproperty("mjClickable", item_r2.Label)("testId", "nav-item-" + item_r2.Label);
45
+ i0.ɵɵattribute("aria-current", ctx_r2.isActive(item_r2) ? "page" : null);
43
46
  i0.ɵɵadvance();
44
47
  i0.ɵɵconditional(item_r2.Icon ? 1 : -1);
45
48
  i0.ɵɵadvance(2);
@@ -280,19 +283,19 @@ export class AppNavComponent {
280
283
  this.navItemDismiss.emit(item);
281
284
  }
282
285
  static ɵfac = function AppNavComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AppNavComponent)(i0.ɵɵdirectiveInject(i1.WorkspaceStateManager), i0.ɵɵdirectiveInject(i2.SharedService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
283
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppNavComponent, selectors: [["mj-app-nav"]], inputs: { app: "app" }, outputs: { navItemClick: "navItemClick", navItemDismiss: "navItemDismiss" }, standalone: false, decls: 3, vars: 2, consts: [[1, "nav-list"], [1, "nav-item", 3, "active", "dynamic", "no-icon"], [1, "nav-item", 3, "click"], [3, "class"], [1, "badge"], ["title", "Remove", 1, "dismiss-btn"], ["title", "Remove", 1, "dismiss-btn", 3, "click"], [1, "fa-solid", "fa-xmark"]], template: function AppNavComponent_Template(rf, ctx) { if (rf & 1) {
286
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppNavComponent, selectors: [["mj-app-nav"]], inputs: { app: "app" }, outputs: { navItemClick: "navItemClick", navItemDismiss: "navItemDismiss" }, standalone: false, decls: 3, vars: 2, consts: [[1, "nav-list"], ["role", "link", 1, "nav-item", 3, "mjClickable", "testId", "active", "dynamic", "no-icon"], ["role", "link", 1, "nav-item", 3, "click", "mjClickable", "testId"], [3, "class"], [1, "badge"], ["title", "Remove", 1, "dismiss-btn"], ["title", "Remove", 1, "dismiss-btn", 3, "click"], [1, "fa-solid", "fa-xmark"]], template: function AppNavComponent_Template(rf, ctx) { if (rf & 1) {
284
287
  i0.ɵɵelementStart(0, "nav", 0);
285
- i0.ɵɵrepeaterCreate(1, AppNavComponent_For_2_Template, 6, 10, "div", 1, ctx.trackByNavItem, true);
288
+ i0.ɵɵrepeaterCreate(1, AppNavComponent_For_2_Template, 6, 13, "div", 1, ctx.trackByNavItem, true);
286
289
  i0.ɵɵelementEnd();
287
290
  } if (rf & 2) {
288
291
  i0.ɵɵstyleProp("--app-color", ctx.appColor);
289
292
  i0.ɵɵadvance();
290
293
  i0.ɵɵrepeater(ctx.navItems);
291
- } }, styles: [".nav-list[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border-radius: 6px;\n text-decoration: none;\n color: var(--mj-text-secondary);\n font-weight: 500;\n font-size: 14px;\n transition: all 0.15s;\n position: relative;\n cursor: pointer;\n user-select: none;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--app-color, var(--mj-brand-primary)) 12%, var(--mj-bg-surface));\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.no-icon[_ngcontent-%COMP%] {\n padding-left: 12px;\n padding-right: 12px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: white;\n border-radius: 10px;\n padding: 2px 6px;\n font-size: 11px;\n font-weight: 600;\n min-width: 18px;\n text-align: center;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.dynamic) + .nav-item.dynamic[_ngcontent-%COMP%] {\n margin-left: 4px;\n padding-left: 16px;\n border-left: 1px solid var(--mj-border-default);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active) {\n opacity: 0.7;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active):hover {\n opacity: 1;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n transition: all 0.15s;\n margin-left: -2px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:hover .dismiss-btn[_ngcontent-%COMP%] {\n display: flex;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n}\n\n\n\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 2px;\n padding: 0 12px;\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n padding: 12px 16px;\n border-radius: 8px;\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n}"], changeDetection: 0 });
294
+ } }, dependencies: [i3.MJClickableDirective], styles: [".nav-list[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border-radius: 6px;\n text-decoration: none;\n color: var(--mj-text-secondary);\n font-weight: 500;\n font-size: 14px;\n transition: all 0.15s;\n position: relative;\n cursor: pointer;\n user-select: none;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--app-color, var(--mj-brand-primary)) 12%, var(--mj-bg-surface));\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.no-icon[_ngcontent-%COMP%] {\n padding-left: 12px;\n padding-right: 12px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: white;\n border-radius: 10px;\n padding: 2px 6px;\n font-size: 11px;\n font-weight: 600;\n min-width: 18px;\n text-align: center;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.dynamic) + .nav-item.dynamic[_ngcontent-%COMP%] {\n margin-left: 4px;\n padding-left: 16px;\n border-left: 1px solid var(--mj-border-default);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active) {\n opacity: 0.7;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active):hover {\n opacity: 1;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n transition: all 0.15s;\n margin-left: -2px;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:hover .dismiss-btn[_ngcontent-%COMP%] {\n display: flex;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n}\n\n\n\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 2px;\n padding: 0 12px;\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n padding: 12px 16px;\n border-radius: 8px;\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n}"], changeDetection: 0 });
292
295
  }
293
296
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppNavComponent, [{
294
297
  type: Component,
295
- args: [{ standalone: false, selector: 'mj-app-nav', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"nav-list\" [style.--app-color]=\"appColor\">\n @for (item of navItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-item\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n [class.no-icon]=\"!item.Icon\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n</nav>\n", styles: [".nav-list {\n display: flex;\n gap: 4px;\n}\n.nav-list .nav-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border-radius: 6px;\n text-decoration: none;\n color: var(--mj-text-secondary);\n font-weight: 500;\n font-size: 14px;\n transition: all 0.15s;\n position: relative;\n cursor: pointer;\n user-select: none;\n}\n.nav-list .nav-item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-list .nav-item.active {\n background: color-mix(in srgb, var(--app-color, var(--mj-brand-primary)) 12%, var(--mj-bg-surface));\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list .nav-item.active i {\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list .nav-item i {\n font-size: 16px;\n}\n.nav-list .nav-item.no-icon {\n padding-left: 12px;\n padding-right: 12px;\n}\n.nav-list .nav-item .badge {\n background: var(--mj-status-error);\n color: white;\n border-radius: 10px;\n padding: 2px 6px;\n font-size: 11px;\n font-weight: 600;\n min-width: 18px;\n text-align: center;\n}\n\n/* Dynamic nav items (recent orphan resources) \u2014 separator and dimming */\n.nav-list .nav-item:not(.dynamic) + .nav-item.dynamic {\n margin-left: 4px;\n padding-left: 16px;\n border-left: 1px solid var(--mj-border-default);\n}\n.nav-list .nav-item.dynamic:not(.active) {\n opacity: 0.7;\n}\n.nav-list .nav-item.dynamic:not(.active):hover {\n opacity: 1;\n}\n\n/* Dismiss button for dynamic nav items */\n.nav-list .nav-item .dismiss-btn {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n transition: all 0.15s;\n margin-left: -2px;\n}\n.nav-list .nav-item.dynamic:hover .dismiss-btn {\n display: flex;\n}\n.nav-list .nav-item .dismiss-btn:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n}\n\n/* Mobile drawer vertical layout */\n:host-context(.mobile-nav-content) .nav-list {\n flex-direction: column;\n gap: 2px;\n padding: 0 12px;\n}\n:host-context(.mobile-nav-content) .nav-list .nav-item {\n padding: 12px 16px;\n border-radius: 8px;\n}\n:host-context(.mobile-nav-content) .nav-list .nav-item i {\n width: 20px;\n text-align: center;\n}\n"] }]
298
+ args: [{ standalone: false, selector: 'mj-app-nav', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"nav-list\" [style.--app-color]=\"appColor\">\n @for (item of navItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-item\"\n [mjClickable]=\"item.Label\"\n role=\"link\"\n [testId]=\"'nav-item-' + item.Label\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n [class.no-icon]=\"!item.Icon\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n</nav>\n", styles: [".nav-list {\n display: flex;\n gap: 4px;\n}\n.nav-list .nav-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border-radius: 6px;\n text-decoration: none;\n color: var(--mj-text-secondary);\n font-weight: 500;\n font-size: 14px;\n transition: all 0.15s;\n position: relative;\n cursor: pointer;\n user-select: none;\n}\n.nav-list .nav-item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-list .nav-item.active {\n background: color-mix(in srgb, var(--app-color, var(--mj-brand-primary)) 12%, var(--mj-bg-surface));\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list .nav-item.active i {\n color: var(--app-color, var(--mj-brand-primary));\n}\n.nav-list .nav-item i {\n font-size: 16px;\n}\n.nav-list .nav-item.no-icon {\n padding-left: 12px;\n padding-right: 12px;\n}\n.nav-list .nav-item .badge {\n background: var(--mj-status-error);\n color: white;\n border-radius: 10px;\n padding: 2px 6px;\n font-size: 11px;\n font-weight: 600;\n min-width: 18px;\n text-align: center;\n}\n\n/* Dynamic nav items (recent orphan resources) \u2014 separator and dimming */\n.nav-list .nav-item:not(.dynamic) + .nav-item.dynamic {\n margin-left: 4px;\n padding-left: 16px;\n border-left: 1px solid var(--mj-border-default);\n}\n.nav-list .nav-item.dynamic:not(.active) {\n opacity: 0.7;\n}\n.nav-list .nav-item.dynamic:not(.active):hover {\n opacity: 1;\n}\n\n/* Dismiss button for dynamic nav items */\n.nav-list .nav-item .dismiss-btn {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n transition: all 0.15s;\n margin-left: -2px;\n}\n.nav-list .nav-item.dynamic:hover .dismiss-btn {\n display: flex;\n}\n.nav-list .nav-item .dismiss-btn:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n}\n\n/* Mobile drawer vertical layout */\n:host-context(.mobile-nav-content) .nav-list {\n flex-direction: column;\n gap: 2px;\n padding: 0 12px;\n}\n:host-context(.mobile-nav-content) .nav-list .nav-item {\n padding: 12px 16px;\n border-radius: 8px;\n}\n:host-context(.mobile-nav-content) .nav-list .nav-item i {\n width: 20px;\n text-align: center;\n}\n"] }]
296
299
  }], () => [{ type: i1.WorkspaceStateManager }, { type: i2.SharedService }, { type: i0.ChangeDetectorRef }], { navItemClick: [{
297
300
  type: Output
298
301
  }], navItemDismiss: [{
@@ -1 +1 @@
1
- {"version":3,"file":"app-nav.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-nav.component.ts","../../../../../src/lib/shell/components/header/app-nav.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { BaseApplication, DynamicNavItem, NavItem, WorkspaceStateManager, WorkspaceConfiguration } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { Subject, takeUntil } from 'rxjs';\n\n/**\n * Event emitted when a nav item is clicked\n */\nexport interface NavItemClickEvent {\n item: NavItem;\n shiftKey: boolean;\n}\n\n/**\n * Horizontal navigation items for the current app.\n * Uses OnPush change detection and reactive state management for optimal performance.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-nav',\n templateUrl: './app-nav.component.html',\n styleUrls: ['./app-nav.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AppNavComponent implements OnInit, OnDestroy {\n private destroy$ = new Subject<void>();\n private _app: BaseApplication | null = null;\n private _cachedNavItems: NavItem[] = [];\n private _cachedAppColor: string = 'var(--mj-brand-primary)';\n private _servicesInjected = false;\n\n /**\n * Monotonically increasing counter used to detect and discard stale async results.\n *\n * Because GetNavItems() is async (HomeApplication does a DB lookup for record names),\n * and RxJS subscribe() does NOT serialize async callbacks, multiple calls to\n * updateCachedData() can overlap. Without this guard, a slow call (e.g., Home app\n * doing a DB lookup) that started BEFORE a fast call (e.g., switching to App B)\n * could resolve AFTER the fast call and overwrite the correct nav items with stale ones.\n *\n * How it works:\n * 1. Each updateCachedData() call increments this counter and captures it as `gen`\n * 2. After the await, it checks: does `gen` still match `_updateGeneration`?\n * 3. If not, a newer call started while we were waiting — discard our stale results\n */\n private _updateGeneration = 0;\n\n // Map of nav item key (Route or Label) to active state\n private activeStateMap = new Map<string, boolean>();\n\n @Output() navItemClick = new EventEmitter<NavItemClickEvent>();\n @Output() navItemDismiss = new EventEmitter<NavItem>();\n\n constructor(\n private workspaceManager: WorkspaceStateManager,\n private sharedService: SharedService,\n private cdr: ChangeDetectorRef\n ) {}\n\n /**\n * Input setter for app - triggers cache update when app changes\n */\n @Input()\n set app(value: BaseApplication | null) {\n if (this._app !== value) {\n this._app = value;\n this._cachedNavItems = []; // Clear stale items immediately so previous app's items don't flash\n this.activeStateMap.clear();\n this._servicesInjected = false; // Reset injection flag\n this.updateCachedData();\n this.cdr.markForCheck();\n }\n }\n\n get app(): BaseApplication | null {\n return this._app;\n }\n\n ngOnInit(): void {\n // Subscribe to workspace configuration changes.\n // Must rebuild nav items (not just active states) because dynamic nav items\n // are generated based on the currently active tab - when a user navigates\n // from one record to another (e.g., via OpenEntityRecord), the active tab\n // changes and the dynamic nav item needs to reflect the new record.\n this.workspaceManager.Configuration\n .pipe(takeUntil(this.destroy$))\n .subscribe(async () => {\n await this.updateCachedData();\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Update cached nav items and app color when app changes\n */\n private async updateCachedData(): Promise<void> {\n // Capture the current generation before any async work.\n // See _updateGeneration JSDoc for full explanation of the race condition this prevents.\n const gen = ++this._updateGeneration;\n\n if (this._app) {\n // Inject services once for apps that need them (e.g., HomeApplication for dynamic nav items)\n if (!this._servicesInjected) {\n const appWithServices = this._app as BaseApplication & {\n SetWorkspaceManager?: (manager: WorkspaceStateManager) => void;\n SetSharedService?: (service: SharedService) => void;\n };\n\n if (typeof appWithServices.SetWorkspaceManager === 'function') {\n appWithServices.SetWorkspaceManager(this.workspaceManager);\n }\n if (typeof appWithServices.SetSharedService === 'function') {\n appWithServices.SetSharedService(this.sharedService);\n }\n this._servicesInjected = true;\n }\n\n const items = await this._app.GetNavItems() || [];\n\n // If a newer call started while we were awaiting, our results are stale — bail out\n // so we don't overwrite the newer call's (correct) results.\n if (gen !== this._updateGeneration) {\n return;\n }\n\n // Only show items with Status 'Active' or undefined (default to Active)\n this._cachedNavItems = items.filter(item => !item.Status || item.Status === 'Active');\n\n this._cachedAppColor = this._app.GetColor() || 'var(--mj-brand-primary)';\n } else {\n this._cachedNavItems = [];\n this._cachedAppColor = 'var(--mj-brand-primary)';\n }\n\n // Update active states after nav items change\n const config = this.workspaceManager.GetConfiguration();\n this.updateActiveStates(config);\n this.cdr.markForCheck();\n\n // In Angular 21 zoneless mode, markForCheck() alone is unreliable when the trigger\n // is an RxJS subscription (workspaceManager.Configuration here) not tracked by the\n // zoneless scheduler — the dirty flag is set but no follow-up tick is scheduled.\n // detectChanges() runs CD synchronously on this view, rendering the new data\n // immediately. Wrapped because detectChanges throws if invoked re-entrantly during\n // another in-flight CD pass — harmless if so.\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless, the in-flight pass picks up our markForCheck.\n }\n }\n\n /**\n * Update active state map based on current workspace configuration\n */\n private updateActiveStates(config: WorkspaceConfiguration | null): void {\n this.activeStateMap.clear();\n\n if (!config || !this._app) {\n return;\n }\n\n const activeTab = config.tabs.find(t => t.id === config.activeTabId);\n if (!activeTab || activeTab.applicationId !== this._app.ID) {\n return;\n }\n\n // Compute active state for each nav item once\n for (const item of this._cachedNavItems) {\n const key = this.getItemKey(item);\n const isActive = this.computeIsActive(item, activeTab);\n this.activeStateMap.set(key, isActive);\n }\n }\n\n /**\n * Get unique key for nav item (used for tracking and active state).\n * Prefers RecordID for dynamic items to avoid label collisions.\n */\n private getItemKey(item: NavItem): string {\n return item.RecordID || item.Route || item.Label || '';\n }\n\n /**\n * Check if a nav item is dynamic (generated from recent orphan resources)\n */\n isDynamic(item: NavItem): boolean {\n return (item as DynamicNavItem).isDynamic === true;\n }\n\n /**\n * Compute if nav item is active based on active tab\n */\n private computeIsActive(item: NavItem, activeTab: any): boolean {\n // Check if nav item has a custom matching function (for dynamic items)\n const dynamicItem = item as NavItem & { isActiveMatch?: (tab: unknown) => boolean };\n if (dynamicItem.isActiveMatch && typeof dynamicItem.isActiveMatch === 'function') {\n return dynamicItem.isActiveMatch(activeTab);\n }\n\n const config = activeTab.configuration || {};\n\n // Match by DriverClass (most reliable for Custom resource types — always set correctly)\n if (item.DriverClass && (config['driverClass'] === item.DriverClass || config['resourceTypeDriverClass'] === item.DriverClass)) {\n return true;\n }\n\n // Match by navItemName from config (reliable — set when nav item opens)\n if (config['navItemName'] && config['navItemName'] === item.Label) {\n return true;\n }\n\n // Match by route (for route-based nav items)\n if (item.Route && config['route'] === item.Route) {\n return true;\n }\n\n // NOTE: We intentionally do NOT match by activeTab.title here.\n // Tab titles can be stale (updated asynchronously by DisplayNameChangedEvent\n // from cached components) and cause double-matches where two nav items\n // both appear active. DriverClass and navItemName are sufficient.\n return false;\n }\n\n /**\n * Get cached navigation items (no computation in getter)\n */\n get navItems(): NavItem[] {\n return this._cachedNavItems;\n }\n\n /**\n * Get cached app color (no computation in getter)\n */\n get appColor(): string {\n return this._cachedAppColor;\n }\n\n /**\n * Check if nav item is active (uses cached state from Map)\n */\n isActive(item: NavItem): boolean {\n const key = this.getItemKey(item);\n return this.activeStateMap.get(key) || false;\n }\n\n /**\n * Track function for @for to optimize rendering\n */\n trackByNavItem(_index: number, item: NavItem): string {\n return this.getItemKey(item);\n }\n\n /**\n * Handle nav item click\n */\n onNavClick(item: NavItem, event?: MouseEvent): void {\n this.navItemClick.emit({\n item,\n shiftKey: event?.shiftKey || false\n });\n }\n\n /**\n * Handle dismiss click on a dynamic nav item.\n * Removes from the app's recent stack and refreshes nav items immediately.\n * Stops propagation so the nav click handler doesn't fire.\n */\n onDismiss(item: NavItem, event: MouseEvent): void {\n event.stopPropagation();\n\n // Remove from the app's recent stack directly so we can refresh immediately\n if (this._app) {\n const appWithRemove = this._app as BaseApplication & {\n RemoveDynamicNavItem?: (navItem: NavItem) => void;\n };\n if (typeof appWithRemove.RemoveDynamicNavItem === 'function') {\n appWithRemove.RemoveDynamicNavItem(item);\n this.updateCachedData();\n }\n }\n\n this.navItemDismiss.emit(item);\n }\n\n}\n","<nav class=\"nav-list\" [style.--app-color]=\"appColor\">\n @for (item of navItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-item\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n [class.no-icon]=\"!item.Icon\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n</nav>\n"]}
1
+ {"version":3,"file":"app-nav.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-nav.component.ts","../../../../../src/lib/shell/components/header/app-nav.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { BaseApplication, DynamicNavItem, NavItem, WorkspaceStateManager, WorkspaceConfiguration } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { Subject, takeUntil } from 'rxjs';\n\n/**\n * Event emitted when a nav item is clicked\n */\nexport interface NavItemClickEvent {\n item: NavItem;\n shiftKey: boolean;\n}\n\n/**\n * Horizontal navigation items for the current app.\n * Uses OnPush change detection and reactive state management for optimal performance.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-nav',\n templateUrl: './app-nav.component.html',\n styleUrls: ['./app-nav.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AppNavComponent implements OnInit, OnDestroy {\n private destroy$ = new Subject<void>();\n private _app: BaseApplication | null = null;\n private _cachedNavItems: NavItem[] = [];\n private _cachedAppColor: string = 'var(--mj-brand-primary)';\n private _servicesInjected = false;\n\n /**\n * Monotonically increasing counter used to detect and discard stale async results.\n *\n * Because GetNavItems() is async (HomeApplication does a DB lookup for record names),\n * and RxJS subscribe() does NOT serialize async callbacks, multiple calls to\n * updateCachedData() can overlap. Without this guard, a slow call (e.g., Home app\n * doing a DB lookup) that started BEFORE a fast call (e.g., switching to App B)\n * could resolve AFTER the fast call and overwrite the correct nav items with stale ones.\n *\n * How it works:\n * 1. Each updateCachedData() call increments this counter and captures it as `gen`\n * 2. After the await, it checks: does `gen` still match `_updateGeneration`?\n * 3. If not, a newer call started while we were waiting — discard our stale results\n */\n private _updateGeneration = 0;\n\n // Map of nav item key (Route or Label) to active state\n private activeStateMap = new Map<string, boolean>();\n\n @Output() navItemClick = new EventEmitter<NavItemClickEvent>();\n @Output() navItemDismiss = new EventEmitter<NavItem>();\n\n constructor(\n private workspaceManager: WorkspaceStateManager,\n private sharedService: SharedService,\n private cdr: ChangeDetectorRef\n ) {}\n\n /**\n * Input setter for app - triggers cache update when app changes\n */\n @Input()\n set app(value: BaseApplication | null) {\n if (this._app !== value) {\n this._app = value;\n this._cachedNavItems = []; // Clear stale items immediately so previous app's items don't flash\n this.activeStateMap.clear();\n this._servicesInjected = false; // Reset injection flag\n this.updateCachedData();\n this.cdr.markForCheck();\n }\n }\n\n get app(): BaseApplication | null {\n return this._app;\n }\n\n ngOnInit(): void {\n // Subscribe to workspace configuration changes.\n // Must rebuild nav items (not just active states) because dynamic nav items\n // are generated based on the currently active tab - when a user navigates\n // from one record to another (e.g., via OpenEntityRecord), the active tab\n // changes and the dynamic nav item needs to reflect the new record.\n this.workspaceManager.Configuration\n .pipe(takeUntil(this.destroy$))\n .subscribe(async () => {\n await this.updateCachedData();\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Update cached nav items and app color when app changes\n */\n private async updateCachedData(): Promise<void> {\n // Capture the current generation before any async work.\n // See _updateGeneration JSDoc for full explanation of the race condition this prevents.\n const gen = ++this._updateGeneration;\n\n if (this._app) {\n // Inject services once for apps that need them (e.g., HomeApplication for dynamic nav items)\n if (!this._servicesInjected) {\n const appWithServices = this._app as BaseApplication & {\n SetWorkspaceManager?: (manager: WorkspaceStateManager) => void;\n SetSharedService?: (service: SharedService) => void;\n };\n\n if (typeof appWithServices.SetWorkspaceManager === 'function') {\n appWithServices.SetWorkspaceManager(this.workspaceManager);\n }\n if (typeof appWithServices.SetSharedService === 'function') {\n appWithServices.SetSharedService(this.sharedService);\n }\n this._servicesInjected = true;\n }\n\n const items = await this._app.GetNavItems() || [];\n\n // If a newer call started while we were awaiting, our results are stale — bail out\n // so we don't overwrite the newer call's (correct) results.\n if (gen !== this._updateGeneration) {\n return;\n }\n\n // Only show items with Status 'Active' or undefined (default to Active)\n this._cachedNavItems = items.filter(item => !item.Status || item.Status === 'Active');\n\n this._cachedAppColor = this._app.GetColor() || 'var(--mj-brand-primary)';\n } else {\n this._cachedNavItems = [];\n this._cachedAppColor = 'var(--mj-brand-primary)';\n }\n\n // Update active states after nav items change\n const config = this.workspaceManager.GetConfiguration();\n this.updateActiveStates(config);\n this.cdr.markForCheck();\n\n // In Angular 21 zoneless mode, markForCheck() alone is unreliable when the trigger\n // is an RxJS subscription (workspaceManager.Configuration here) not tracked by the\n // zoneless scheduler — the dirty flag is set but no follow-up tick is scheduled.\n // detectChanges() runs CD synchronously on this view, rendering the new data\n // immediately. Wrapped because detectChanges throws if invoked re-entrantly during\n // another in-flight CD pass — harmless if so.\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless, the in-flight pass picks up our markForCheck.\n }\n }\n\n /**\n * Update active state map based on current workspace configuration\n */\n private updateActiveStates(config: WorkspaceConfiguration | null): void {\n this.activeStateMap.clear();\n\n if (!config || !this._app) {\n return;\n }\n\n const activeTab = config.tabs.find(t => t.id === config.activeTabId);\n if (!activeTab || activeTab.applicationId !== this._app.ID) {\n return;\n }\n\n // Compute active state for each nav item once\n for (const item of this._cachedNavItems) {\n const key = this.getItemKey(item);\n const isActive = this.computeIsActive(item, activeTab);\n this.activeStateMap.set(key, isActive);\n }\n }\n\n /**\n * Get unique key for nav item (used for tracking and active state).\n * Prefers RecordID for dynamic items to avoid label collisions.\n */\n private getItemKey(item: NavItem): string {\n return item.RecordID || item.Route || item.Label || '';\n }\n\n /**\n * Check if a nav item is dynamic (generated from recent orphan resources)\n */\n isDynamic(item: NavItem): boolean {\n return (item as DynamicNavItem).isDynamic === true;\n }\n\n /**\n * Compute if nav item is active based on active tab\n */\n private computeIsActive(item: NavItem, activeTab: any): boolean {\n // Check if nav item has a custom matching function (for dynamic items)\n const dynamicItem = item as NavItem & { isActiveMatch?: (tab: unknown) => boolean };\n if (dynamicItem.isActiveMatch && typeof dynamicItem.isActiveMatch === 'function') {\n return dynamicItem.isActiveMatch(activeTab);\n }\n\n const config = activeTab.configuration || {};\n\n // Match by DriverClass (most reliable for Custom resource types — always set correctly)\n if (item.DriverClass && (config['driverClass'] === item.DriverClass || config['resourceTypeDriverClass'] === item.DriverClass)) {\n return true;\n }\n\n // Match by navItemName from config (reliable — set when nav item opens)\n if (config['navItemName'] && config['navItemName'] === item.Label) {\n return true;\n }\n\n // Match by route (for route-based nav items)\n if (item.Route && config['route'] === item.Route) {\n return true;\n }\n\n // NOTE: We intentionally do NOT match by activeTab.title here.\n // Tab titles can be stale (updated asynchronously by DisplayNameChangedEvent\n // from cached components) and cause double-matches where two nav items\n // both appear active. DriverClass and navItemName are sufficient.\n return false;\n }\n\n /**\n * Get cached navigation items (no computation in getter)\n */\n get navItems(): NavItem[] {\n return this._cachedNavItems;\n }\n\n /**\n * Get cached app color (no computation in getter)\n */\n get appColor(): string {\n return this._cachedAppColor;\n }\n\n /**\n * Check if nav item is active (uses cached state from Map)\n */\n isActive(item: NavItem): boolean {\n const key = this.getItemKey(item);\n return this.activeStateMap.get(key) || false;\n }\n\n /**\n * Track function for @for to optimize rendering\n */\n trackByNavItem(_index: number, item: NavItem): string {\n return this.getItemKey(item);\n }\n\n /**\n * Handle nav item click\n */\n onNavClick(item: NavItem, event?: MouseEvent): void {\n this.navItemClick.emit({\n item,\n shiftKey: event?.shiftKey || false\n });\n }\n\n /**\n * Handle dismiss click on a dynamic nav item.\n * Removes from the app's recent stack and refreshes nav items immediately.\n * Stops propagation so the nav click handler doesn't fire.\n */\n onDismiss(item: NavItem, event: MouseEvent): void {\n event.stopPropagation();\n\n // Remove from the app's recent stack directly so we can refresh immediately\n if (this._app) {\n const appWithRemove = this._app as BaseApplication & {\n RemoveDynamicNavItem?: (navItem: NavItem) => void;\n };\n if (typeof appWithRemove.RemoveDynamicNavItem === 'function') {\n appWithRemove.RemoveDynamicNavItem(item);\n this.updateCachedData();\n }\n }\n\n this.navItemDismiss.emit(item);\n }\n\n}\n","<nav class=\"nav-list\" [style.--app-color]=\"appColor\">\n @for (item of navItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-item\"\n [mjClickable]=\"item.Label\"\n role=\"link\"\n [testId]=\"'nav-item-' + item.Label\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n [class.no-icon]=\"!item.Icon\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n</nav>\n"]}
@@ -2,7 +2,8 @@ import { Component, Input, Output, EventEmitter, HostListener, ViewChild } from
2
2
  import { UUIDsEqual } from '@memberjunction/global';
3
3
  import * as i0 from "@angular/core";
4
4
  import * as i1 from "@memberjunction/ng-base-application";
5
- import * as i2 from "@memberjunction/ng-explorer-settings";
5
+ import * as i2 from "@memberjunction/ng-ui-components";
6
+ import * as i3 from "@memberjunction/ng-explorer-settings";
6
7
  const _c0 = ["appConfigDialog"];
7
8
  function AppSwitcherComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
8
9
  i0.ɵɵelement(0, "i", 3);
@@ -37,6 +38,8 @@ function AppSwitcherComponent_Conditional_5_For_3_Template(rf, ctx) { if (rf & 1
37
38
  const ctx_r1 = i0.ɵɵnextContext(2);
38
39
  i0.ɵɵstyleProp("--item-color", app_r5.GetColor());
39
40
  i0.ɵɵclassProp("active", ctx_r1.IsActiveApp(app_r5))("loading", ctx_r1.IsLoadingApp(app_r5));
41
+ i0.ɵɵproperty("mjClickable", app_r5.Name)("testId", "app-switcher-item-" + app_r5.Name);
42
+ i0.ɵɵattribute("aria-current", ctx_r1.IsActiveApp(app_r5) ? "true" : null);
40
43
  i0.ɵɵadvance();
41
44
  i0.ɵɵconditional(ctx_r1.IsLoadingApp(app_r5) ? 1 : -1);
42
45
  i0.ɵɵadvance();
@@ -47,7 +50,7 @@ function AppSwitcherComponent_Conditional_5_For_3_Template(rf, ctx) { if (rf & 1
47
50
  function AppSwitcherComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
48
51
  const _r3 = i0.ɵɵgetCurrentView();
49
52
  i0.ɵɵelementStart(0, "div", 6)(1, "div", 9);
50
- i0.ɵɵrepeaterCreate(2, AppSwitcherComponent_Conditional_5_For_3_Template, 5, 9, "div", 10, i0.ɵɵrepeaterTrackByIdentity);
53
+ i0.ɵɵrepeaterCreate(2, AppSwitcherComponent_Conditional_5_For_3_Template, 5, 12, "div", 10, i0.ɵɵrepeaterTrackByIdentity);
51
54
  i0.ɵɵelementEnd();
52
55
  i0.ɵɵelement(4, "div", 11);
53
56
  i0.ɵɵelementStart(5, "div", 12);
@@ -153,7 +156,7 @@ export class AppSwitcherComponent {
153
156
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.appConfigDialog = _t.first);
154
157
  } }, hostBindings: function AppSwitcherComponent_HostBindings(rf, ctx) { if (rf & 1) {
155
158
  i0.ɵɵlistener("click", function AppSwitcherComponent_click_HostBindingHandler($event) { return ctx.onClickOutside($event); }, i0.ɵɵresolveDocument);
156
- } }, inputs: { activeApp: "activeApp", isViewingSystemTab: "isViewingSystemTab", loadingAppId: "loadingAppId" }, outputs: { appSelected: "appSelected" }, standalone: false, decls: 8, vars: 6, consts: [["appConfigDialog", ""], [1, "app-switcher-container"], [1, "app-switcher-button", 3, "click"], [1, "app-icon", "loading-spinner", "fa-solid", "fa-spinner", "fa-spin"], [1, "app-icon", 3, "class", "color", "--active-color"], [1, "dropdown-arrow", "fa-solid", "fa-chevron-down"], [1, "app-switcher-dropdown"], [3, "ShowDialogChange", "ConfigSaved", "ShowDialog"], [1, "app-icon"], [1, "app-switcher-list"], [1, "app-switcher-item", 3, "active", "loading", "--item-color"], [1, "app-switcher-divider"], [1, "app-switcher-item", "configure-item", 3, "click"], [1, "fa-solid", "fa-gear"], [1, "app-switcher-item", 3, "click"], [1, "fa-solid", "fa-spinner", "fa-spin"], [3, "class"]], template: function AppSwitcherComponent_Template(rf, ctx) { if (rf & 1) {
159
+ } }, inputs: { activeApp: "activeApp", isViewingSystemTab: "isViewingSystemTab", loadingAppId: "loadingAppId" }, outputs: { appSelected: "appSelected" }, standalone: false, decls: 8, vars: 8, consts: [["appConfigDialog", ""], [1, "app-switcher-container"], ["mjClickable", "Switch application", "testId", "app-switcher-trigger", 1, "app-switcher-button", 3, "click"], [1, "app-icon", "loading-spinner", "fa-solid", "fa-spinner", "fa-spin"], [1, "app-icon", 3, "class", "color", "--active-color"], [1, "dropdown-arrow", "fa-solid", "fa-chevron-down"], [1, "app-switcher-dropdown"], [3, "ShowDialogChange", "ConfigSaved", "ShowDialog"], [1, "app-icon"], [1, "app-switcher-list"], [1, "app-switcher-item", 3, "mjClickable", "testId", "active", "loading", "--item-color"], [1, "app-switcher-divider"], ["mjClickable", "Configure applications", "testId", "app-switcher-configure", 1, "app-switcher-item", "configure-item", 3, "click"], [1, "fa-solid", "fa-gear"], [1, "app-switcher-item", 3, "click", "mjClickable", "testId"], [1, "fa-solid", "fa-spinner", "fa-spin"], [3, "class"]], template: function AppSwitcherComponent_Template(rf, ctx) { if (rf & 1) {
157
160
  const _r1 = i0.ɵɵgetCurrentView();
158
161
  i0.ɵɵelementStart(0, "div", 1)(1, "div", 2);
159
162
  i0.ɵɵlistener("click", function AppSwitcherComponent_Template_div_click_1_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.toggleDropdown()); });
@@ -169,7 +172,9 @@ export class AppSwitcherComponent {
169
172
  i0.ɵɵelementEnd();
170
173
  } if (rf & 2) {
171
174
  i0.ɵɵclassProp("loading", ctx.isLoading);
172
- i0.ɵɵadvance(2);
175
+ i0.ɵɵadvance();
176
+ i0.ɵɵattribute("aria-haspopup", true)("aria-expanded", ctx.showDropdown);
177
+ i0.ɵɵadvance();
173
178
  i0.ɵɵconditional(ctx.isLoading ? 2 : -1);
174
179
  i0.ɵɵadvance();
175
180
  i0.ɵɵconditional(!ctx.isLoading ? 3 : -1);
@@ -177,11 +182,11 @@ export class AppSwitcherComponent {
177
182
  i0.ɵɵconditional(ctx.showDropdown ? 5 : -1);
178
183
  i0.ɵɵadvance();
179
184
  i0.ɵɵtwoWayProperty("ShowDialog", ctx.showConfigDialog);
180
- } }, dependencies: [i2.UserAppConfigComponent], styles: [".app-switcher-container[_ngcontent-%COMP%] {\n position: relative;\n margin-left: -2px; \n\n}\n\n.app-switcher-button[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s;\n user-select: none;\n position: relative;\n}\n.app-switcher-button[_ngcontent-%COMP%] .app-icon[_ngcontent-%COMP%] {\n font-size: 20px;\n width: 24px;\n text-align: center;\n position: relative;\n}\n\n\n.app-switcher-button[_ngcontent-%COMP%] .app-icon[_ngcontent-%COMP%]::after {\n content: \"\";\n position: absolute;\n bottom: -6px;\n left: 2px;\n right: 2px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--active-color, var(--mj-text-muted));\n}\n.app-switcher-button[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-button[_ngcontent-%COMP%] .dropdown-arrow[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-disabled);\n}\n\n.app-switcher-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: calc(100% + 8px);\n left: 0;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 220px;\n width: max-content;\n overflow: hidden;\n z-index: 10000;\n max-height: calc(100vh - 80px - var(--mj-connectivity-banner-height, 0px));\n display: flex;\n flex-direction: column;\n}\n.app-switcher-list[_ngcontent-%COMP%] {\n overflow-y: auto;\n flex: 1 1 auto;\n min-height: 0;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.15s;\n font-weight: 500;\n color: var(--mj-text-secondary);\n position: relative;\n white-space: nowrap;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%]::before {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 8px;\n bottom: 8px;\n width: 3px;\n border-radius: 2px;\n background-color: var(--item-color, var(--mj-text-muted));\n opacity: 0.8;\n transition: all 0.15s;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 18px;\n color: var(--item-color, var(--mj-text-muted));\n width: 20px;\n text-align: center;\n opacity: 1;\n transition: all 0.15s;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%]:hover::before {\n opacity: 1;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--item-color, var(--mj-brand-primary)) 10%, var(--mj-bg-surface-elevated));\n color: var(--item-color, var(--mj-brand-primary));\n font-weight: 600;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.active[_ngcontent-%COMP%]::before {\n width: 4px;\n left: 3px;\n opacity: 1;\n}\n\n\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%]::before {\n display: none;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-divider[_ngcontent-%COMP%] {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 12px;\n}\n\n\n\n.app-switcher-button[_ngcontent-%COMP%] .loading-spinner[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n.app-switcher-button[_ngcontent-%COMP%] .loading-spinner[_ngcontent-%COMP%]::after {\n \n\n display: none;\n}\n\n\n\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.loading[_ngcontent-%COMP%] {\n pointer-events: none;\n opacity: 0.7;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.loading[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--item-color, var(--mj-text-muted));\n}\n\n\n\n@media (max-width: 600px) {\n .app-switcher-dropdown[_ngcontent-%COMP%] {\n position: fixed;\n top: calc(60px + var(--mj-connectivity-banner-height, 0px));\n left: 0;\n right: 0;\n width: 100%;\n min-width: unset;\n height: calc(100vh - 60px - var(--mj-connectivity-banner-height, 0px));\n overflow-y: auto;\n border-radius: 0;\n border-top: none;\n padding: 0.25rem 0;\n }\n\n .app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] {\n padding: 14px 20px;\n gap: 14px;\n min-height: 48px;\n font-size: 0.9375rem;\n }\n\n .app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n width: 24px;\n }\n\n .app-switcher-list[_ngcontent-%COMP%] {\n overflow-y: visible;\n flex: none;\n }\n}"] });
185
+ } }, dependencies: [i2.MJClickableDirective, i3.UserAppConfigComponent], styles: [".app-switcher-container[_ngcontent-%COMP%] {\n position: relative;\n margin-left: -2px; \n\n}\n\n.app-switcher-button[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s;\n user-select: none;\n position: relative;\n}\n.app-switcher-button[_ngcontent-%COMP%] .app-icon[_ngcontent-%COMP%] {\n font-size: 20px;\n width: 24px;\n text-align: center;\n position: relative;\n}\n\n\n.app-switcher-button[_ngcontent-%COMP%] .app-icon[_ngcontent-%COMP%]::after {\n content: \"\";\n position: absolute;\n bottom: -6px;\n left: 2px;\n right: 2px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--active-color, var(--mj-text-muted));\n}\n.app-switcher-button[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-button[_ngcontent-%COMP%] .dropdown-arrow[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-disabled);\n}\n\n.app-switcher-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: calc(100% + 8px);\n left: 0;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 220px;\n width: max-content;\n overflow: hidden;\n z-index: 10000;\n max-height: calc(100vh - 80px - var(--mj-connectivity-banner-height, 0px));\n display: flex;\n flex-direction: column;\n}\n.app-switcher-list[_ngcontent-%COMP%] {\n overflow-y: auto;\n flex: 1 1 auto;\n min-height: 0;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.15s;\n font-weight: 500;\n color: var(--mj-text-secondary);\n position: relative;\n white-space: nowrap;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%]::before {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 8px;\n bottom: 8px;\n width: 3px;\n border-radius: 2px;\n background-color: var(--item-color, var(--mj-text-muted));\n opacity: 0.8;\n transition: all 0.15s;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 18px;\n color: var(--item-color, var(--mj-text-muted));\n width: 20px;\n text-align: center;\n opacity: 1;\n transition: all 0.15s;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%]:hover::before {\n opacity: 1;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--item-color, var(--mj-brand-primary)) 10%, var(--mj-bg-surface-elevated));\n color: var(--item-color, var(--mj-brand-primary));\n font-weight: 600;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.active[_ngcontent-%COMP%]::before {\n width: 4px;\n left: 3px;\n opacity: 1;\n}\n\n\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%]::before {\n display: none;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.configure-item[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-divider[_ngcontent-%COMP%] {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 12px;\n}\n\n\n\n.app-switcher-button[_ngcontent-%COMP%] .loading-spinner[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n.app-switcher-button[_ngcontent-%COMP%] .loading-spinner[_ngcontent-%COMP%]::after {\n \n\n display: none;\n}\n\n\n\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.loading[_ngcontent-%COMP%] {\n pointer-events: none;\n opacity: 0.7;\n}\n.app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item.loading[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--item-color, var(--mj-text-muted));\n}\n\n\n\n@media (max-width: 600px) {\n .app-switcher-dropdown[_ngcontent-%COMP%] {\n position: fixed;\n top: calc(60px + var(--mj-connectivity-banner-height, 0px));\n left: 0;\n right: 0;\n width: 100%;\n min-width: unset;\n height: calc(100vh - 60px - var(--mj-connectivity-banner-height, 0px));\n overflow-y: auto;\n border-radius: 0;\n border-top: none;\n padding: 0.25rem 0;\n }\n\n .app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] {\n padding: 14px 20px;\n gap: 14px;\n min-height: 48px;\n font-size: 0.9375rem;\n }\n\n .app-switcher-dropdown[_ngcontent-%COMP%] .app-switcher-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n width: 24px;\n }\n\n .app-switcher-list[_ngcontent-%COMP%] {\n overflow-y: visible;\n flex: none;\n }\n}"] });
181
186
  }
182
187
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppSwitcherComponent, [{
183
188
  type: Component,
184
- args: [{ standalone: false, selector: 'mj-app-switcher', template: "<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n (click)=\"toggleDropdown()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\"\n [style.--active-color]=\"activeApp?.GetColor()\"></i>\n }\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\"></i>\n </div>\n\n @if (showDropdown) {\n <div class=\"app-switcher-dropdown\">\n <div class=\"app-switcher-list\">\n @for (app of apps; track app) {\n <div\n class=\"app-switcher-item\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--item-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!IsLoadingApp(app)) {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\"></i>\n }\n <span>{{ app.Name }}</span>\n </div>\n }\n </div>\n <!-- Divider + Configure pinned at bottom -->\n <div class=\"app-switcher-divider\"></div>\n <div class=\"app-switcher-item configure-item\" (click)=\"openConfigDialog()\">\n <i class=\"fa-solid fa-gear\"></i>\n <span>Configure...</span>\n </div>\n </div>\n }\n</div>\n\n<!-- App Configuration Dialog -->\n<mj-user-app-config\n #appConfigDialog\n [(ShowDialog)]=\"showConfigDialog\"\n (ConfigSaved)=\"onConfigSaved()\">\n</mj-user-app-config>\n", styles: [".app-switcher-container {\n position: relative;\n margin-left: -2px; /* Fine-tune alignment when no nav-bar icons to the left */\n}\n\n.app-switcher-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s;\n user-select: none;\n position: relative;\n}\n.app-switcher-button .app-icon {\n font-size: 20px;\n width: 24px;\n text-align: center;\n position: relative;\n}\n/* Color underline under the icon only, not the caret */\n.app-switcher-button .app-icon::after {\n content: \"\";\n position: absolute;\n bottom: -6px;\n left: 2px;\n right: 2px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--active-color, var(--mj-text-muted));\n}\n.app-switcher-button:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-button .dropdown-arrow {\n font-size: 10px;\n color: var(--mj-text-disabled);\n}\n\n.app-switcher-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n left: 0;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 220px;\n width: max-content;\n overflow: hidden;\n z-index: 10000;\n max-height: calc(100vh - 80px - var(--mj-connectivity-banner-height, 0px));\n display: flex;\n flex-direction: column;\n}\n.app-switcher-list {\n overflow-y: auto;\n flex: 1 1 auto;\n min-height: 0;\n}\n.app-switcher-dropdown .app-switcher-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.15s;\n font-weight: 500;\n color: var(--mj-text-secondary);\n position: relative;\n white-space: nowrap;\n}\n.app-switcher-dropdown .app-switcher-item::before {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 8px;\n bottom: 8px;\n width: 3px;\n border-radius: 2px;\n background-color: var(--item-color, var(--mj-text-muted));\n opacity: 0.8;\n transition: all 0.15s;\n}\n.app-switcher-dropdown .app-switcher-item i {\n font-size: 18px;\n color: var(--item-color, var(--mj-text-muted));\n width: 20px;\n text-align: center;\n opacity: 1;\n transition: all 0.15s;\n}\n.app-switcher-dropdown .app-switcher-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-dropdown .app-switcher-item:hover::before {\n opacity: 1;\n}\n.app-switcher-dropdown .app-switcher-item.active {\n background: color-mix(in srgb, var(--item-color, var(--mj-brand-primary)) 10%, var(--mj-bg-surface-elevated));\n color: var(--item-color, var(--mj-brand-primary));\n font-weight: 600;\n}\n.app-switcher-dropdown .app-switcher-item.active::before {\n width: 4px;\n left: 3px;\n opacity: 1;\n}\n/* Configure option - no color line, neutral gray styling */\n.app-switcher-dropdown .app-switcher-item.configure-item {\n color: var(--mj-text-secondary);\n}\n.app-switcher-dropdown .app-switcher-item.configure-item::before {\n display: none;\n}\n.app-switcher-dropdown .app-switcher-item.configure-item i {\n color: var(--mj-text-muted);\n}\n.app-switcher-dropdown .app-switcher-item.configure-item:hover {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown .app-switcher-item.configure-item:hover i {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown .app-switcher-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 12px;\n}\n\n/* Loading state for app switcher button */\n.app-switcher-button .loading-spinner {\n color: var(--mj-text-muted);\n}\n.app-switcher-button .loading-spinner::after {\n /* Hide the underline when showing spinner */\n display: none;\n}\n\n/* Loading state for dropdown items */\n.app-switcher-dropdown .app-switcher-item.loading {\n pointer-events: none;\n opacity: 0.7;\n}\n.app-switcher-dropdown .app-switcher-item.loading i {\n color: var(--item-color, var(--mj-text-muted));\n}\n\n/* Mobile: full-width dropdown with scroll */\n@media (max-width: 600px) {\n .app-switcher-dropdown {\n position: fixed;\n top: calc(60px + var(--mj-connectivity-banner-height, 0px));\n left: 0;\n right: 0;\n width: 100%;\n min-width: unset;\n height: calc(100vh - 60px - var(--mj-connectivity-banner-height, 0px));\n overflow-y: auto;\n border-radius: 0;\n border-top: none;\n padding: 0.25rem 0;\n }\n\n .app-switcher-dropdown .app-switcher-item {\n padding: 14px 20px;\n gap: 14px;\n min-height: 48px;\n font-size: 0.9375rem;\n }\n\n .app-switcher-dropdown .app-switcher-item i {\n font-size: 20px;\n width: 24px;\n }\n\n .app-switcher-list {\n overflow-y: visible;\n flex: none;\n }\n}\n"] }]
189
+ args: [{ standalone: false, selector: 'mj-app-switcher', template: "<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n mjClickable=\"Switch application\"\n testId=\"app-switcher-trigger\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"showDropdown\"\n (click)=\"toggleDropdown()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\"\n [style.--active-color]=\"activeApp?.GetColor()\"></i>\n }\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\"></i>\n </div>\n\n @if (showDropdown) {\n <div class=\"app-switcher-dropdown\">\n <div class=\"app-switcher-list\">\n @for (app of apps; track app) {\n <div\n class=\"app-switcher-item\"\n [mjClickable]=\"app.Name\"\n [testId]=\"'app-switcher-item-' + app.Name\"\n [attr.aria-current]=\"IsActiveApp(app) ? 'true' : null\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--item-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!IsLoadingApp(app)) {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\"></i>\n }\n <span>{{ app.Name }}</span>\n </div>\n }\n </div>\n <!-- Divider + Configure pinned at bottom -->\n <div class=\"app-switcher-divider\"></div>\n <div class=\"app-switcher-item configure-item\" mjClickable=\"Configure applications\" testId=\"app-switcher-configure\" (click)=\"openConfigDialog()\">\n <i class=\"fa-solid fa-gear\"></i>\n <span>Configure...</span>\n </div>\n </div>\n }\n</div>\n\n<!-- App Configuration Dialog -->\n<mj-user-app-config\n #appConfigDialog\n [(ShowDialog)]=\"showConfigDialog\"\n (ConfigSaved)=\"onConfigSaved()\">\n</mj-user-app-config>\n", styles: [".app-switcher-container {\n position: relative;\n margin-left: -2px; /* Fine-tune alignment when no nav-bar icons to the left */\n}\n\n.app-switcher-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s;\n user-select: none;\n position: relative;\n}\n.app-switcher-button .app-icon {\n font-size: 20px;\n width: 24px;\n text-align: center;\n position: relative;\n}\n/* Color underline under the icon only, not the caret */\n.app-switcher-button .app-icon::after {\n content: \"\";\n position: absolute;\n bottom: -6px;\n left: 2px;\n right: 2px;\n height: 3px;\n border-radius: 2px;\n background-color: var(--active-color, var(--mj-text-muted));\n}\n.app-switcher-button:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-button .dropdown-arrow {\n font-size: 10px;\n color: var(--mj-text-disabled);\n}\n\n.app-switcher-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n left: 0;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-lg);\n min-width: 220px;\n width: max-content;\n overflow: hidden;\n z-index: 10000;\n max-height: calc(100vh - 80px - var(--mj-connectivity-banner-height, 0px));\n display: flex;\n flex-direction: column;\n}\n.app-switcher-list {\n overflow-y: auto;\n flex: 1 1 auto;\n min-height: 0;\n}\n.app-switcher-dropdown .app-switcher-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.15s;\n font-weight: 500;\n color: var(--mj-text-secondary);\n position: relative;\n white-space: nowrap;\n}\n.app-switcher-dropdown .app-switcher-item::before {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 8px;\n bottom: 8px;\n width: 3px;\n border-radius: 2px;\n background-color: var(--item-color, var(--mj-text-muted));\n opacity: 0.8;\n transition: all 0.15s;\n}\n.app-switcher-dropdown .app-switcher-item i {\n font-size: 18px;\n color: var(--item-color, var(--mj-text-muted));\n width: 20px;\n text-align: center;\n opacity: 1;\n transition: all 0.15s;\n}\n.app-switcher-dropdown .app-switcher-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n.app-switcher-dropdown .app-switcher-item:hover::before {\n opacity: 1;\n}\n.app-switcher-dropdown .app-switcher-item.active {\n background: color-mix(in srgb, var(--item-color, var(--mj-brand-primary)) 10%, var(--mj-bg-surface-elevated));\n color: var(--item-color, var(--mj-brand-primary));\n font-weight: 600;\n}\n.app-switcher-dropdown .app-switcher-item.active::before {\n width: 4px;\n left: 3px;\n opacity: 1;\n}\n/* Configure option - no color line, neutral gray styling */\n.app-switcher-dropdown .app-switcher-item.configure-item {\n color: var(--mj-text-secondary);\n}\n.app-switcher-dropdown .app-switcher-item.configure-item::before {\n display: none;\n}\n.app-switcher-dropdown .app-switcher-item.configure-item i {\n color: var(--mj-text-muted);\n}\n.app-switcher-dropdown .app-switcher-item.configure-item:hover {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown .app-switcher-item.configure-item:hover i {\n color: var(--mj-text-primary);\n}\n.app-switcher-dropdown .app-switcher-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 12px;\n}\n\n/* Loading state for app switcher button */\n.app-switcher-button .loading-spinner {\n color: var(--mj-text-muted);\n}\n.app-switcher-button .loading-spinner::after {\n /* Hide the underline when showing spinner */\n display: none;\n}\n\n/* Loading state for dropdown items */\n.app-switcher-dropdown .app-switcher-item.loading {\n pointer-events: none;\n opacity: 0.7;\n}\n.app-switcher-dropdown .app-switcher-item.loading i {\n color: var(--item-color, var(--mj-text-muted));\n}\n\n/* Mobile: full-width dropdown with scroll */\n@media (max-width: 600px) {\n .app-switcher-dropdown {\n position: fixed;\n top: calc(60px + var(--mj-connectivity-banner-height, 0px));\n left: 0;\n right: 0;\n width: 100%;\n min-width: unset;\n height: calc(100vh - 60px - var(--mj-connectivity-banner-height, 0px));\n overflow-y: auto;\n border-radius: 0;\n border-top: none;\n padding: 0.25rem 0;\n }\n\n .app-switcher-dropdown .app-switcher-item {\n padding: 14px 20px;\n gap: 14px;\n min-height: 48px;\n font-size: 0.9375rem;\n }\n\n .app-switcher-dropdown .app-switcher-item i {\n font-size: 20px;\n width: 24px;\n }\n\n .app-switcher-list {\n overflow-y: visible;\n flex: none;\n }\n}\n"] }]
185
190
  }], () => [{ type: i1.ApplicationManager }], { activeApp: [{
186
191
  type: Input
187
192
  }], isViewingSystemTab: [{
@@ -1 +1 @@
1
- {"version":3,"file":"app-switcher.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-switcher.component.ts","../../../../../src/lib/shell/components/header/app-switcher.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGhG,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;;;;;;ICE9C,uBAAoE;;;IAGpE,uBAEmD;;;IAF/B,8FAA+C;IAEnE,AADE,sFAAqC,iFACO;;;IAgBtC,wBAA2C;;;IAG3C,oBAAgD;;;IAA7C,gDAAwC;;;;IAV/C,+BAK2B;IAAzB,qNAAS,wBAAc,KAAC;IACxB,yGAAyB;IAGzB,yGAA0B;IAG1B,4BAAM;IAAA,YAAc;IACtB,AADsB,iBAAO,EACvB;;;;IATJ,iDAAqC;IADrC,AADA,oDAAiC,wCACE;IAGnC,cAEC;IAFD,sDAEC;IACD,cAEC;IAFD,uDAEC;IACK,eAAc;IAAd,iCAAc;;;;IAd1B,AADF,8BAAmC,aACF;IAC7B,wHAeC;IACH,iBAAM;IAEN,0BAAwC;IACxC,+BAA2E;IAA7B,qLAAS,yBAAkB,KAAC;IACxE,wBAAgC;IAChC,4BAAM;IAAA,4BAAY;IAEtB,AADE,AADoB,iBAAO,EACrB,EACF;;;IAvBF,eAeC;IAfD,0BAeC;;AD5BT;;;GAGG;AAOH,MAAM,OAAO,oBAAoB;IAYX;IAXX,SAAS,GAA2B,IAAI,CAAC;IACzC,kBAAkB,GAAG,KAAK,CAAC;IACpC,qEAAqE;IAC5D,YAAY,GAAkB,IAAI,CAAC;IAClC,WAAW,GAAG,IAAI,YAAY,EAAU,CAAC;IAErB,eAAe,CAA0B;IAEvE,YAAY,GAAG,KAAK,CAAC;IACrB,gBAAgB,GAAG,KAAK,CAAC;IAEzB,YAAoB,UAA8B;QAA9B,eAAU,GAAV,UAAU,CAAoB;IAAG,CAAC;IAEtD;;OAEG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,GAAoB;QAC5B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACvE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,WAAW,CAAC,GAAoB;QAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,+EAA+E;IAC/E,YAAY,CAAC,GAAoB;QAC/B,OAAO,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IAEH,cAAc,CAAC,KAAiB;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,yBAAyB,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACpE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,kDAAkD;QAClD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;YAC9B,CAAC;QACH,CAAC,EAAE,CAAC,CAAC,CAAC;IACR,CAAC;IAED;;OAEG;IACH,aAAa;QACX,yDAAyD;QACzD,mDAAmD;QACnD,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;8GAzFU,oBAAoB;6DAApB,oBAAoB;;;;;;YAApB,+FAAA,0BAAsB,0BAAF;;;YCd/B,AADF,8BAAgE,aAEjC;YAA3B,oIAAS,oBAAgB,KAAC;YAE1B,oFAAiB;YAGjB,oFAAkB;YAKlB,uBAAuD;YACzD,iBAAM;YAEN,sFAAoB;YA4BtB,iBAAM;YAGN,gDAGkC;YADhC,wRAAiC;YACjC,+JAAe,mBAAe,KAAC;YACjC,iBAAqB;;YAlDe,wCAA2B;YAI3D,eAEC;YAFD,wCAEC;YACD,cAIC;YAJD,yCAIC;YAIH,eA2BC;YA3BD,2CA2BC;YAMD,cAAiC;YAAjC,uDAAiC;;;iFDjCtB,oBAAoB;cANhC,SAAS;6BACI,KAAK,YACP,iBAAiB;;kBAK1B,KAAK;;kBACL,KAAK;;kBAEL,KAAK;;kBACL,MAAM;;kBAEN,SAAS;mBAAC,iBAAiB;;kBAqD3B,YAAY;mBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;kFA5D/B,oBAAoB","sourcesContent":["import { Component, Input, Output, EventEmitter, HostListener, ViewChild } from '@angular/core';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { UserAppConfigComponent } from '@memberjunction/ng-explorer-settings';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * App switcher dropdown in the header.\n * Displays current app and allows switching between apps.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-switcher',\n templateUrl: './app-switcher.component.html',\n styleUrls: ['./app-switcher.component.css']\n})\nexport class AppSwitcherComponent {\n @Input() activeApp: BaseApplication | null = null;\n @Input() isViewingSystemTab = false;\n /** ID of the app currently being loaded (shows loading indicator) */\n @Input() loadingAppId: string | null = null;\n @Output() appSelected = new EventEmitter<string>();\n\n @ViewChild('appConfigDialog') appConfigDialog!: UserAppConfigComponent;\n\n showDropdown = false;\n showConfigDialog = false;\n\n constructor(private appManager: ApplicationManager) {}\n\n /**\n * Check if the app switcher should show loading state\n */\n get isLoading(): boolean {\n return this.loadingAppId !== null;\n }\n\n /**\n * Get applications that should appear in the app switcher dropdown\n * (NavigationStyle = 'App Switcher' or 'Both')\n */\n get apps(): BaseApplication[] {\n return this.appManager.GetAppSwitcherApps();\n }\n\n /**\n * Toggle dropdown visibility\n */\n toggleDropdown(): void {\n this.showDropdown = !this.showDropdown;\n }\n\n /**\n * Select an application\n * When viewing a system tab, always emit to allow returning to the app\n */\n selectApp(app: BaseApplication): void {\n this.showDropdown = false;\n if (!UUIDsEqual(app.ID, this.activeApp?.ID) || this.isViewingSystemTab) {\n this.appSelected.emit(app.ID);\n }\n }\n\n /** Case-insensitive UUID check whether an app is the currently active app. */\n IsActiveApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.activeApp?.ID);\n }\n\n /** Case-insensitive UUID check whether an app is the one currently loading. */\n IsLoadingApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.loadingAppId);\n }\n\n /**\n * Close dropdown when clicking outside\n */\n @HostListener('document:click', ['$event'])\n onClickOutside(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n if (!target.closest('.app-switcher-container') && this.showDropdown) {\n this.showDropdown = false;\n }\n }\n\n /**\n * Open the app configuration dialog\n */\n openConfigDialog(): void {\n this.showDropdown = false;\n this.showConfigDialog = true;\n // Use setTimeout to ensure ViewChild is available\n setTimeout(() => {\n if (this.appConfigDialog) {\n this.appConfigDialog.Open();\n }\n }, 0);\n }\n\n /**\n * Handle when config is saved - reload the app list\n */\n onConfigSaved(): void {\n // The ApplicationManager will be refreshed by the dialog\n // Force a re-render by triggering change detection\n this.showConfigDialog = false;\n }\n}\n","<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n (click)=\"toggleDropdown()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\"\n [style.--active-color]=\"activeApp?.GetColor()\"></i>\n }\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\"></i>\n </div>\n\n @if (showDropdown) {\n <div class=\"app-switcher-dropdown\">\n <div class=\"app-switcher-list\">\n @for (app of apps; track app) {\n <div\n class=\"app-switcher-item\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--item-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!IsLoadingApp(app)) {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\"></i>\n }\n <span>{{ app.Name }}</span>\n </div>\n }\n </div>\n <!-- Divider + Configure pinned at bottom -->\n <div class=\"app-switcher-divider\"></div>\n <div class=\"app-switcher-item configure-item\" (click)=\"openConfigDialog()\">\n <i class=\"fa-solid fa-gear\"></i>\n <span>Configure...</span>\n </div>\n </div>\n }\n</div>\n\n<!-- App Configuration Dialog -->\n<mj-user-app-config\n #appConfigDialog\n [(ShowDialog)]=\"showConfigDialog\"\n (ConfigSaved)=\"onConfigSaved()\">\n</mj-user-app-config>\n"]}
1
+ {"version":3,"file":"app-switcher.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-switcher.component.ts","../../../../../src/lib/shell/components/header/app-switcher.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, HostListener, ViewChild } from '@angular/core';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { UserAppConfigComponent } from '@memberjunction/ng-explorer-settings';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * App switcher dropdown in the header.\n * Displays current app and allows switching between apps.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-switcher',\n templateUrl: './app-switcher.component.html',\n styleUrls: ['./app-switcher.component.css']\n})\nexport class AppSwitcherComponent {\n @Input() activeApp: BaseApplication | null = null;\n @Input() isViewingSystemTab = false;\n /** ID of the app currently being loaded (shows loading indicator) */\n @Input() loadingAppId: string | null = null;\n @Output() appSelected = new EventEmitter<string>();\n\n @ViewChild('appConfigDialog') appConfigDialog!: UserAppConfigComponent;\n\n showDropdown = false;\n showConfigDialog = false;\n\n constructor(private appManager: ApplicationManager) {}\n\n /**\n * Check if the app switcher should show loading state\n */\n get isLoading(): boolean {\n return this.loadingAppId !== null;\n }\n\n /**\n * Get applications that should appear in the app switcher dropdown\n * (NavigationStyle = 'App Switcher' or 'Both')\n */\n get apps(): BaseApplication[] {\n return this.appManager.GetAppSwitcherApps();\n }\n\n /**\n * Toggle dropdown visibility\n */\n toggleDropdown(): void {\n this.showDropdown = !this.showDropdown;\n }\n\n /**\n * Select an application\n * When viewing a system tab, always emit to allow returning to the app\n */\n selectApp(app: BaseApplication): void {\n this.showDropdown = false;\n if (!UUIDsEqual(app.ID, this.activeApp?.ID) || this.isViewingSystemTab) {\n this.appSelected.emit(app.ID);\n }\n }\n\n /** Case-insensitive UUID check whether an app is the currently active app. */\n IsActiveApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.activeApp?.ID);\n }\n\n /** Case-insensitive UUID check whether an app is the one currently loading. */\n IsLoadingApp(app: BaseApplication): boolean {\n return UUIDsEqual(app.ID, this.loadingAppId);\n }\n\n /**\n * Close dropdown when clicking outside\n */\n @HostListener('document:click', ['$event'])\n onClickOutside(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n if (!target.closest('.app-switcher-container') && this.showDropdown) {\n this.showDropdown = false;\n }\n }\n\n /**\n * Open the app configuration dialog\n */\n openConfigDialog(): void {\n this.showDropdown = false;\n this.showConfigDialog = true;\n // Use setTimeout to ensure ViewChild is available\n setTimeout(() => {\n if (this.appConfigDialog) {\n this.appConfigDialog.Open();\n }\n }, 0);\n }\n\n /**\n * Handle when config is saved - reload the app list\n */\n onConfigSaved(): void {\n // The ApplicationManager will be refreshed by the dialog\n // Force a re-render by triggering change detection\n this.showConfigDialog = false;\n }\n}\n","<div class=\"app-switcher-container\" [class.loading]=\"isLoading\">\n <div class=\"app-switcher-button\"\n mjClickable=\"Switch application\"\n testId=\"app-switcher-trigger\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"showDropdown\"\n (click)=\"toggleDropdown()\">\n <!-- Show spinner when loading, otherwise show app icon -->\n @if (isLoading) {\n <i class=\"app-icon loading-spinner fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!isLoading) {\n <i class=\"app-icon\" [class]=\"activeApp?.Icon || 'fa-solid fa-cube'\"\n [style.color]=\"activeApp?.GetColor()\"\n [style.--active-color]=\"activeApp?.GetColor()\"></i>\n }\n <i class=\"dropdown-arrow fa-solid fa-chevron-down\"></i>\n </div>\n\n @if (showDropdown) {\n <div class=\"app-switcher-dropdown\">\n <div class=\"app-switcher-list\">\n @for (app of apps; track app) {\n <div\n class=\"app-switcher-item\"\n [mjClickable]=\"app.Name\"\n [testId]=\"'app-switcher-item-' + app.Name\"\n [attr.aria-current]=\"IsActiveApp(app) ? 'true' : null\"\n [class.active]=\"IsActiveApp(app)\"\n [class.loading]=\"IsLoadingApp(app)\"\n [style.--item-color]=\"app.GetColor()\"\n (click)=\"selectApp(app)\">\n @if (IsLoadingApp(app)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!IsLoadingApp(app)) {\n <i [class]=\"app.Icon || 'fa-solid fa-cube'\"></i>\n }\n <span>{{ app.Name }}</span>\n </div>\n }\n </div>\n <!-- Divider + Configure pinned at bottom -->\n <div class=\"app-switcher-divider\"></div>\n <div class=\"app-switcher-item configure-item\" mjClickable=\"Configure applications\" testId=\"app-switcher-configure\" (click)=\"openConfigDialog()\">\n <i class=\"fa-solid fa-gear\"></i>\n <span>Configure...</span>\n </div>\n </div>\n }\n</div>\n\n<!-- App Configuration Dialog -->\n<mj-user-app-config\n #appConfigDialog\n [(ShowDialog)]=\"showConfigDialog\"\n (ConfigSaved)=\"onConfigSaved()\">\n</mj-user-app-config>\n"]}
@@ -13,7 +13,7 @@ import * as i11 from "@memberjunction/ng-shared-generic";
13
13
  import * as i12 from "@memberjunction/ng-search";
14
14
  export declare class ShellModule {
15
15
  static ɵfac: i0.ɵɵFactoryDeclaration<ShellModule, never>;
16
- static ɵmod: i0.ɵɵNgModuleDeclaration<ShellModule, [typeof i1.ShellComponent, typeof i2.AppSwitcherComponent, typeof i3.AppNavComponent, typeof i4.TabContainerComponent, typeof i5.AppAccessDialogComponent, typeof i6.CommandPaletteComponent], [typeof i7.CommonModule, typeof i8.FormsModule, typeof i9.MJDropdownComponent, typeof i10.ExplorerSettingsModule, typeof i11.SharedGenericModule, typeof i12.SearchModule], [typeof i1.ShellComponent]>;
16
+ static ɵmod: i0.ɵɵNgModuleDeclaration<ShellModule, [typeof i1.ShellComponent, typeof i2.AppSwitcherComponent, typeof i3.AppNavComponent, typeof i4.TabContainerComponent, typeof i5.AppAccessDialogComponent, typeof i6.CommandPaletteComponent], [typeof i7.CommonModule, typeof i8.FormsModule, typeof i9.MJDropdownComponent, typeof i9.MJClickableDirective, typeof i10.ExplorerSettingsModule, typeof i11.SharedGenericModule, typeof i12.SearchModule], [typeof i1.ShellComponent]>;
17
17
  static ɵinj: i0.ɵɵInjectorDeclaration<ShellModule>;
18
18
  }
19
19
  //# sourceMappingURL=shell.module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shell.module.d.ts","sourceRoot":"","sources":["../../../src/lib/shell/shell.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAeA,qBAwBa,WAAW;yCAAX,WAAW;0CAAX,WAAW;0CAAX,WAAW;CAAI"}
1
+ {"version":3,"file":"shell.module.d.ts","sourceRoot":"","sources":["../../../src/lib/shell/shell.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAeA,qBAyBa,WAAW;yCAAX,WAAW;0CAAX,WAAW;0CAAX,WAAW;CAAI"}
@@ -10,7 +10,7 @@ import { CommandPaletteComponent } from '../command-palette/command-palette.comp
10
10
  import { CommandPaletteService } from '../command-palette/command-palette.service';
11
11
  import { ExplorerSettingsModule } from '@memberjunction/ng-explorer-settings';
12
12
  import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
13
- import { MJDropdownComponent } from '@memberjunction/ng-ui-components';
13
+ import { MJDropdownComponent, MJClickableDirective } from '@memberjunction/ng-ui-components';
14
14
  import { SearchModule } from '@memberjunction/ng-search';
15
15
  import * as i0 from "@angular/core";
16
16
  export class ShellModule {
@@ -40,6 +40,7 @@ export class ShellModule {
40
40
  CommonModule,
41
41
  FormsModule,
42
42
  MJDropdownComponent,
43
+ MJClickableDirective,
43
44
  ExplorerSettingsModule,
44
45
  SharedGenericModule,
45
46
  SearchModule
@@ -60,6 +61,7 @@ export class ShellModule {
60
61
  CommandPaletteComponent], imports: [CommonModule,
61
62
  FormsModule,
62
63
  MJDropdownComponent,
64
+ MJClickableDirective,
63
65
  ExplorerSettingsModule,
64
66
  SharedGenericModule,
65
67
  SearchModule], exports: [ShellComponent] }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"shell.module.js","sourceRoot":"","sources":["../../../src/lib/shell/shell.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kDAAkD,CAAC;AAC5F,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;;AA0BzD,MAAM,OAAO,WAAW;qGAAX,WAAW;4DAAX,WAAW;iEAPX;YACT,qBAAqB;SACtB,YATC,YAAY;YACZ,WAAW;YACX,mBAAmB;YACnB,sBAAsB;YACtB,mBAAmB;YACnB,YAAY;;iFASH,WAAW;cAxBvB,QAAQ;eAAC;gBACR,YAAY,EAAE;oBACZ,cAAc;oBACd,oBAAoB;oBACpB,eAAe;oBACf,qBAAqB;oBACrB,wBAAwB;oBACxB,uBAAuB;iBACxB;gBACD,OAAO,EAAE;oBACP,YAAY;oBACZ,WAAW;oBACX,mBAAmB;oBACnB,sBAAsB;oBACtB,mBAAmB;oBACnB,YAAY;iBACb;gBACD,SAAS,EAAE;oBACT,qBAAqB;iBACtB;gBACD,OAAO,EAAE;oBACP,cAAc;iBACf;aACF;;wFACY,WAAW,mBAtBpB,cAAc;QACd,oBAAoB;QACpB,eAAe;QACf,qBAAqB;QACrB,wBAAwB;QACxB,uBAAuB,aAGvB,YAAY;QACZ,WAAW;QACX,mBAAmB;QACnB,sBAAsB;QACtB,mBAAmB;QACnB,YAAY,aAMZ,cAAc","sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ShellComponent } from './shell.component';\nimport { AppSwitcherComponent } from './components/header/app-switcher.component';\nimport { AppNavComponent } from './components/header/app-nav.component';\nimport { TabContainerComponent } from './components/tabs/tab-container.component';\nimport { AppAccessDialogComponent } from './components/dialogs/app-access-dialog.component';\nimport { CommandPaletteComponent } from '../command-palette/command-palette.component';\nimport { CommandPaletteService } from '../command-palette/command-palette.service';\nimport { ExplorerSettingsModule } from '@memberjunction/ng-explorer-settings';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\nimport { MJDropdownComponent } from '@memberjunction/ng-ui-components';\nimport { SearchModule } from '@memberjunction/ng-search';\n\n@NgModule({\n declarations: [\n ShellComponent,\n AppSwitcherComponent,\n AppNavComponent,\n TabContainerComponent,\n AppAccessDialogComponent,\n CommandPaletteComponent\n ],\n imports: [\n CommonModule,\n FormsModule,\n MJDropdownComponent,\n ExplorerSettingsModule,\n SharedGenericModule,\n SearchModule\n ],\n providers: [\n CommandPaletteService\n ],\n exports: [\n ShellComponent\n ]\n})\nexport class ShellModule { }\n"]}
1
+ {"version":3,"file":"shell.module.js","sourceRoot":"","sources":["../../../src/lib/shell/shell.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kDAAkD,CAAC;AAC5F,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;;AA2BzD,MAAM,OAAO,WAAW;qGAAX,WAAW;4DAAX,WAAW;iEAPX;YACT,qBAAqB;SACtB,YAVC,YAAY;YACZ,WAAW;YACX,mBAAmB;YAEnB,sBAAsB;YACtB,mBAAmB;YACnB,YAAY;;iFASH,WAAW;cAzBvB,QAAQ;eAAC;gBACR,YAAY,EAAE;oBACZ,cAAc;oBACd,oBAAoB;oBACpB,eAAe;oBACf,qBAAqB;oBACrB,wBAAwB;oBACxB,uBAAuB;iBACxB;gBACD,OAAO,EAAE;oBACP,YAAY;oBACZ,WAAW;oBACX,mBAAmB;oBACnB,oBAAoB;oBACpB,sBAAsB;oBACtB,mBAAmB;oBACnB,YAAY;iBACb;gBACD,SAAS,EAAE;oBACT,qBAAqB;iBACtB;gBACD,OAAO,EAAE;oBACP,cAAc;iBACf;aACF;;wFACY,WAAW,mBAvBpB,cAAc;QACd,oBAAoB;QACpB,eAAe;QACf,qBAAqB;QACrB,wBAAwB;QACxB,uBAAuB,aAGvB,YAAY;QACZ,WAAW;QACX,mBAAmB;QACnB,oBAAoB;QACpB,sBAAsB;QACtB,mBAAmB;QACnB,YAAY,aAMZ,cAAc","sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { ShellComponent } from './shell.component';\nimport { AppSwitcherComponent } from './components/header/app-switcher.component';\nimport { AppNavComponent } from './components/header/app-nav.component';\nimport { TabContainerComponent } from './components/tabs/tab-container.component';\nimport { AppAccessDialogComponent } from './components/dialogs/app-access-dialog.component';\nimport { CommandPaletteComponent } from '../command-palette/command-palette.component';\nimport { CommandPaletteService } from '../command-palette/command-palette.service';\nimport { ExplorerSettingsModule } from '@memberjunction/ng-explorer-settings';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\nimport { MJDropdownComponent, MJClickableDirective } from '@memberjunction/ng-ui-components';\nimport { SearchModule } from '@memberjunction/ng-search';\n\n@NgModule({\n declarations: [\n ShellComponent,\n AppSwitcherComponent,\n AppNavComponent,\n TabContainerComponent,\n AppAccessDialogComponent,\n CommandPaletteComponent\n ],\n imports: [\n CommonModule,\n FormsModule,\n MJDropdownComponent,\n MJClickableDirective,\n ExplorerSettingsModule,\n SharedGenericModule,\n SearchModule\n ],\n providers: [\n CommandPaletteService\n ],\n exports: [\n ShellComponent\n ]\n})\nexport class ShellModule { }\n"]}
@@ -49,6 +49,12 @@ export declare class UserNotificationsComponent extends BaseAngularComponent imp
49
49
  markAllAsUnread(): Promise<void>;
50
50
  markAll(bRead: boolean): Promise<void>;
51
51
  notificationClicked(notification: MJUserNotificationEntity): void;
52
+ /**
53
+ * Opens the Meet app's Live Room in JOIN mode for a `meet-room` invite notification (the
54
+ * `{ type:'meet-room', room }` ResourceConfiguration), mirroring {@link navigateToConversation}.
55
+ * Returns `false` (not handled) when the config isn't a meet-room invite or the Meet app is absent.
56
+ */
57
+ private navigateToMeetRoom;
52
58
  /**
53
59
  * Navigate to a resource-based notification using NavigationService methods.
54
60
  * Routes to the correct resource based on the notification's ResourceType.
@@ -1 +1 @@
1
- {"version":3,"file":"user-notifications.component.d.ts","sourceRoot":"","sources":["../../../src/lib/user-notifications/user-notifications.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"user-notifications.component.d.ts","sourceRoot":"","sources":["../../../src/lib/user-notifications/user-notifications.component.ts"],"names":[],"mappings":"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"}