@memberjunction/ng-conversations 2.106.0 → 2.108.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 (112) hide show
  1. package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts +67 -0
  2. package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -0
  3. package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +725 -0
  4. package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -0
  5. package/dist/lib/components/collection/artifact-create-modal.component.d.ts +39 -0
  6. package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -0
  7. package/dist/lib/components/collection/artifact-create-modal.component.js +351 -0
  8. package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -0
  9. package/dist/lib/components/collection/collection-form-modal.component.d.ts +3 -1
  10. package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
  11. package/dist/lib/components/collection/collection-form-modal.component.js +60 -10
  12. package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
  13. package/dist/lib/components/collection/collection-share-modal.component.d.ts +43 -0
  14. package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -0
  15. package/dist/lib/components/collection/collection-share-modal.component.js +728 -0
  16. package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -0
  17. package/dist/lib/components/collection/collection-tree.component.d.ts +8 -1
  18. package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
  19. package/dist/lib/components/collection/collection-tree.component.js +217 -115
  20. package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
  21. package/dist/lib/components/collection/collection-view.component.d.ts +2 -1
  22. package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
  23. package/dist/lib/components/collection/collection-view.component.js +52 -34
  24. package/dist/lib/components/collection/collection-view.component.js.map +1 -1
  25. package/dist/lib/components/collection/collections-full-view.component.d.ts +45 -9
  26. package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
  27. package/dist/lib/components/collection/collections-full-view.component.js +586 -220
  28. package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
  29. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +53 -20
  30. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  31. package/dist/lib/components/conversation/conversation-chat-area.component.js +365 -250
  32. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  33. package/dist/lib/components/message/message-input.component.d.ts +71 -1
  34. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  35. package/dist/lib/components/message/message-input.component.js +303 -104
  36. package/dist/lib/components/message/message-input.component.js.map +1 -1
  37. package/dist/lib/components/message/message-item.component.d.ts +39 -5
  38. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  39. package/dist/lib/components/message/message-item.component.js +259 -137
  40. package/dist/lib/components/message/message-item.component.js.map +1 -1
  41. package/dist/lib/components/message/message-list.component.d.ts +8 -6
  42. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  43. package/dist/lib/components/message/message-list.component.js +52 -9
  44. package/dist/lib/components/message/message-list.component.js.map +1 -1
  45. package/dist/lib/components/message/suggested-responses.component.d.ts +55 -0
  46. package/dist/lib/components/message/suggested-responses.component.d.ts.map +1 -0
  47. package/dist/lib/components/message/suggested-responses.component.js +207 -0
  48. package/dist/lib/components/message/suggested-responses.component.js.map +1 -0
  49. package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
  50. package/dist/lib/components/search/search-panel.component.js +245 -113
  51. package/dist/lib/components/search/search-panel.component.js.map +1 -1
  52. package/dist/lib/components/shared/user-picker.component.d.ts +29 -0
  53. package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -0
  54. package/dist/lib/components/shared/user-picker.component.js +229 -0
  55. package/dist/lib/components/shared/user-picker.component.js.map +1 -0
  56. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +7 -1
  57. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
  58. package/dist/lib/components/tasks/tasks-dropdown.component.js +36 -6
  59. package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
  60. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +19 -2
  61. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  62. package/dist/lib/components/workspace/conversation-workspace.component.js +167 -58
  63. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  64. package/dist/lib/conversations.module.d.ts +52 -47
  65. package/dist/lib/conversations.module.d.ts.map +1 -1
  66. package/dist/lib/conversations.module.js +27 -4
  67. package/dist/lib/conversations.module.js.map +1 -1
  68. package/dist/lib/models/conversation-complete-query.model.d.ts +75 -0
  69. package/dist/lib/models/conversation-complete-query.model.d.ts.map +1 -0
  70. package/dist/lib/models/conversation-complete-query.model.js +19 -0
  71. package/dist/lib/models/conversation-complete-query.model.js.map +1 -0
  72. package/dist/lib/models/conversation-state.model.d.ts +27 -0
  73. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  74. package/dist/lib/models/lazy-artifact-info.d.ts +68 -0
  75. package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -0
  76. package/dist/lib/models/lazy-artifact-info.js +150 -0
  77. package/dist/lib/models/lazy-artifact-info.js.map +1 -0
  78. package/dist/lib/services/agent-state.service.d.ts.map +1 -1
  79. package/dist/lib/services/agent-state.service.js +5 -0
  80. package/dist/lib/services/agent-state.service.js.map +1 -1
  81. package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
  82. package/dist/lib/services/artifact-state.service.js +14 -9
  83. package/dist/lib/services/artifact-state.service.js.map +1 -1
  84. package/dist/lib/services/collection-permission.service.d.ts +96 -0
  85. package/dist/lib/services/collection-permission.service.d.ts.map +1 -0
  86. package/dist/lib/services/collection-permission.service.js +303 -0
  87. package/dist/lib/services/collection-permission.service.js.map +1 -0
  88. package/dist/lib/services/collection-state.service.d.ts +34 -0
  89. package/dist/lib/services/collection-state.service.d.ts.map +1 -0
  90. package/dist/lib/services/collection-state.service.js +50 -0
  91. package/dist/lib/services/collection-state.service.js.map +1 -0
  92. package/dist/lib/services/conversation-agent.service.d.ts +20 -4
  93. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  94. package/dist/lib/services/conversation-agent.service.js +179 -17
  95. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  96. package/dist/lib/services/data-cache.service.d.ts.map +1 -1
  97. package/dist/lib/services/data-cache.service.js +5 -0
  98. package/dist/lib/services/data-cache.service.js.map +1 -1
  99. package/dist/lib/services/mention-autocomplete.service.js +1 -1
  100. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  101. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  102. package/dist/lib/services/mention-parser.service.js +0 -5
  103. package/dist/lib/services/mention-parser.service.js.map +1 -1
  104. package/dist/lib/services/search.service.d.ts +26 -3
  105. package/dist/lib/services/search.service.d.ts.map +1 -1
  106. package/dist/lib/services/search.service.js +172 -12
  107. package/dist/lib/services/search.service.js.map +1 -1
  108. package/dist/public-api.d.ts +4 -0
  109. package/dist/public-api.d.ts.map +1 -1
  110. package/dist/public-api.js +4 -0
  111. package/dist/public-api.js.map +1 -1
  112. package/package.json +12 -12
@@ -1,15 +1,19 @@
1
- import { Component, Input } from '@angular/core';
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
2
  import { RunView, Metadata } from '@memberjunction/core';
3
+ import { Subject, takeUntil } from 'rxjs';
3
4
  import * as i0 from "@angular/core";
4
5
  import * as i1 from "../../services/dialog.service";
5
6
  import * as i2 from "../../services/artifact-state.service";
6
- import * as i3 from "@angular/common";
7
- import * as i4 from "@angular/forms";
8
- import * as i5 from "./collection-form-modal.component";
7
+ import * as i3 from "../../services/collection-state.service";
8
+ import * as i4 from "../../services/collection-permission.service";
9
+ import * as i5 from "@angular/common";
10
+ import * as i6 from "./collection-share-modal.component";
11
+ import * as i7 from "./collection-form-modal.component";
12
+ import * as i8 from "./artifact-create-modal.component";
9
13
  function CollectionsFullViewComponent_span_7_ng_container_1_Template(rf, ctx) { if (rf & 1) {
10
14
  const _r1 = i0.ɵɵgetCurrentView();
11
15
  i0.ɵɵelementContainerStart(0);
12
- i0.ɵɵelement(1, "i", 22);
16
+ i0.ɵɵelement(1, "i", 19);
13
17
  i0.ɵɵelementStart(2, "a", 5);
14
18
  i0.ɵɵlistener("click", function CollectionsFullViewComponent_span_7_ng_container_1_Template_a_click_2_listener() { const crumb_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.navigateTo(crumb_r2)); });
15
19
  i0.ɵɵtext(3);
@@ -24,172 +28,221 @@ function CollectionsFullViewComponent_span_7_ng_container_1_Template(rf, ctx) {
24
28
  i0.ɵɵtextInterpolate1(" ", crumb_r2.name, " ");
25
29
  } }
26
30
  function CollectionsFullViewComponent_span_7_Template(rf, ctx) { if (rf & 1) {
27
- i0.ɵɵelementStart(0, "span", 20);
28
- i0.ɵɵtemplate(1, CollectionsFullViewComponent_span_7_ng_container_1_Template, 4, 3, "ng-container", 21);
31
+ i0.ɵɵelementStart(0, "span", 17);
32
+ i0.ɵɵtemplate(1, CollectionsFullViewComponent_span_7_ng_container_1_Template, 4, 3, "ng-container", 18);
29
33
  i0.ɵɵelementEnd();
30
34
  } if (rf & 2) {
31
35
  const ctx_r2 = i0.ɵɵnextContext();
32
36
  i0.ɵɵadvance();
33
37
  i0.ɵɵproperty("ngForOf", ctx_r2.breadcrumbs);
34
38
  } }
35
- function CollectionsFullViewComponent_div_18_Template(rf, ctx) { if (rf & 1) {
36
- i0.ɵɵelementStart(0, "div", 23);
37
- i0.ɵɵelement(1, "i", 24);
39
+ function CollectionsFullViewComponent_button_9_Template(rf, ctx) { if (rf & 1) {
40
+ const _r5 = i0.ɵɵgetCurrentView();
41
+ i0.ɵɵelementStart(0, "button", 20);
42
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_button_9_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.createCollection()); });
43
+ i0.ɵɵelement(1, "i", 21);
44
+ i0.ɵɵtext(2, " New Collection ");
45
+ i0.ɵɵelementEnd();
46
+ } }
47
+ function CollectionsFullViewComponent_div_13_Template(rf, ctx) { if (rf & 1) {
48
+ i0.ɵɵelementStart(0, "div", 22);
49
+ i0.ɵɵelement(1, "i", 23);
38
50
  i0.ɵɵelementStart(2, "p");
39
51
  i0.ɵɵtext(3, "Loading collections...");
40
52
  i0.ɵɵelementEnd()();
41
53
  } }
42
- function CollectionsFullViewComponent_div_19_Template(rf, ctx) { if (rf & 1) {
43
- const _r5 = i0.ɵɵgetCurrentView();
44
- i0.ɵɵelementStart(0, "div", 25);
45
- i0.ɵɵelement(1, "i", 26);
46
- i0.ɵɵelementStart(2, "h3");
47
- i0.ɵɵtext(3, "No collections yet");
54
+ function CollectionsFullViewComponent_div_14_button_7_Template(rf, ctx) { if (rf & 1) {
55
+ const _r6 = i0.ɵɵgetCurrentView();
56
+ i0.ɵɵelementStart(0, "button", 31);
57
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_button_7_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.createCollection()); });
58
+ i0.ɵɵelement(1, "i", 32);
59
+ i0.ɵɵtext(2, " New Collection ");
48
60
  i0.ɵɵelementEnd();
49
- i0.ɵɵelementStart(4, "p");
50
- i0.ɵɵtext(5, "Create your first collection to organize artifacts");
61
+ } }
62
+ function CollectionsFullViewComponent_div_14_div_8_div_1_div_3_Template(rf, ctx) { if (rf & 1) {
63
+ i0.ɵɵelementStart(0, "div", 47);
64
+ i0.ɵɵelement(1, "i", 48);
51
65
  i0.ɵɵelementEnd();
52
- i0.ɵɵelementStart(6, "button", 27);
53
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_19_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r5); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.createCollection()); });
54
- i0.ɵɵelement(7, "i", 12);
55
- i0.ɵɵtext(8, " Create Collection ");
56
- i0.ɵɵelementEnd()();
57
66
  } }
58
- function CollectionsFullViewComponent_div_20_Template(rf, ctx) { if (rf & 1) {
59
- i0.ɵɵelementStart(0, "div", 25);
60
- i0.ɵɵelement(1, "i", 8);
61
- i0.ɵɵelementStart(2, "p");
62
- i0.ɵɵtext(3, "No collections or artifacts found");
67
+ function CollectionsFullViewComponent_div_14_div_8_div_1_div_7_Template(rf, ctx) { if (rf & 1) {
68
+ i0.ɵɵelementStart(0, "div", 49);
69
+ i0.ɵɵelement(1, "i", 50);
70
+ i0.ɵɵelementStart(2, "span");
71
+ i0.ɵɵtext(3);
63
72
  i0.ɵɵelementEnd()();
73
+ } if (rf & 2) {
74
+ const collection_r8 = i0.ɵɵnextContext().$implicit;
75
+ i0.ɵɵadvance(3);
76
+ i0.ɵɵtextInterpolate(collection_r8.Owner);
64
77
  } }
65
- function CollectionsFullViewComponent_div_21_div_1_div_7_div_6_Template(rf, ctx) { if (rf & 1) {
66
- i0.ɵɵelementStart(0, "div", 46);
78
+ function CollectionsFullViewComponent_div_14_div_8_div_1_div_8_Template(rf, ctx) { if (rf & 1) {
79
+ i0.ɵɵelementStart(0, "div", 51);
67
80
  i0.ɵɵtext(1);
68
81
  i0.ɵɵelementEnd();
69
82
  } if (rf & 2) {
70
- const collection_r7 = i0.ɵɵnextContext().$implicit;
83
+ const collection_r8 = i0.ɵɵnextContext().$implicit;
71
84
  i0.ɵɵadvance();
72
- i0.ɵɵtextInterpolate1(" ", collection_r7.Description, " ");
85
+ i0.ɵɵtextInterpolate1(" ", collection_r8.Description, " ");
73
86
  } }
74
- function CollectionsFullViewComponent_div_21_div_1_div_7_Template(rf, ctx) { if (rf & 1) {
75
- const _r6 = i0.ɵɵgetCurrentView();
87
+ function CollectionsFullViewComponent_div_14_div_8_div_1_button_10_Template(rf, ctx) { if (rf & 1) {
88
+ const _r9 = i0.ɵɵgetCurrentView();
89
+ i0.ɵɵelementStart(0, "button", 52);
90
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_8_div_1_button_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r9); const collection_r8 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.shareCollection(collection_r8)); });
91
+ i0.ɵɵelement(1, "i", 53);
92
+ i0.ɵɵelementEnd();
93
+ } }
94
+ function CollectionsFullViewComponent_div_14_div_8_div_1_button_11_Template(rf, ctx) { if (rf & 1) {
95
+ const _r10 = i0.ɵɵgetCurrentView();
96
+ i0.ɵɵelementStart(0, "button", 54);
97
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_8_div_1_button_11_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const collection_r8 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.editCollection(collection_r8)); });
98
+ i0.ɵɵelement(1, "i", 55);
99
+ i0.ɵɵelementEnd();
100
+ } }
101
+ function CollectionsFullViewComponent_div_14_div_8_div_1_button_12_Template(rf, ctx) { if (rf & 1) {
102
+ const _r11 = i0.ɵɵgetCurrentView();
103
+ i0.ɵɵelementStart(0, "button", 56);
104
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_8_div_1_button_12_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r11); const collection_r8 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.deleteCollection(collection_r8)); });
105
+ i0.ɵɵelement(1, "i", 57);
106
+ i0.ɵɵelementEnd();
107
+ } }
108
+ function CollectionsFullViewComponent_div_14_div_8_div_1_Template(rf, ctx) { if (rf & 1) {
109
+ const _r7 = i0.ɵɵgetCurrentView();
76
110
  i0.ɵɵelementStart(0, "div", 35);
77
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_1_div_7_Template_div_click_0_listener() { const collection_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.openCollection(collection_r7)); });
111
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_8_div_1_Template_div_click_0_listener() { const collection_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.openCollection(collection_r8)); });
78
112
  i0.ɵɵelementStart(1, "div", 36);
79
113
  i0.ɵɵelement(2, "i", 37);
114
+ i0.ɵɵtemplate(3, CollectionsFullViewComponent_div_14_div_8_div_1_div_3_Template, 2, 0, "div", 38);
80
115
  i0.ɵɵelementEnd();
81
- i0.ɵɵelementStart(3, "div", 38)(4, "div", 39);
82
- i0.ɵɵtext(5);
83
- i0.ɵɵelementEnd();
84
- i0.ɵɵtemplate(6, CollectionsFullViewComponent_div_21_div_1_div_7_div_6_Template, 2, 1, "div", 40);
116
+ i0.ɵɵelementStart(4, "div", 39)(5, "div", 40);
117
+ i0.ɵɵtext(6);
85
118
  i0.ɵɵelementEnd();
86
- i0.ɵɵelementStart(7, "div", 41);
87
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_1_div_7_Template_div_click_7_listener($event) { i0.ɵɵrestoreView(_r6); return i0.ɵɵresetView($event.stopPropagation()); });
88
- i0.ɵɵelementStart(8, "button", 42);
89
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_1_div_7_Template_button_click_8_listener() { const collection_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.editCollection(collection_r7)); });
90
- i0.ɵɵelement(9, "i", 43);
119
+ i0.ɵɵtemplate(7, CollectionsFullViewComponent_div_14_div_8_div_1_div_7_Template, 4, 1, "div", 41)(8, CollectionsFullViewComponent_div_14_div_8_div_1_div_8_Template, 2, 1, "div", 42);
91
120
  i0.ɵɵelementEnd();
92
- i0.ɵɵelementStart(10, "button", 44);
93
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_1_div_7_Template_button_click_10_listener() { const collection_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.deleteCollection(collection_r7)); });
94
- i0.ɵɵelement(11, "i", 45);
95
- i0.ɵɵelementEnd()()();
121
+ i0.ɵɵelementStart(9, "div", 43);
122
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_8_div_1_Template_div_click_9_listener($event) { i0.ɵɵrestoreView(_r7); return i0.ɵɵresetView($event.stopPropagation()); });
123
+ i0.ɵɵtemplate(10, CollectionsFullViewComponent_div_14_div_8_div_1_button_10_Template, 2, 0, "button", 44)(11, CollectionsFullViewComponent_div_14_div_8_div_1_button_11_Template, 2, 0, "button", 45)(12, CollectionsFullViewComponent_div_14_div_8_div_1_button_12_Template, 2, 0, "button", 46);
124
+ i0.ɵɵelementEnd()();
96
125
  } if (rf & 2) {
97
- const collection_r7 = ctx.$implicit;
98
- i0.ɵɵadvance(5);
99
- i0.ɵɵtextInterpolate(collection_r7.Name);
126
+ const collection_r8 = ctx.$implicit;
127
+ const ctx_r2 = i0.ɵɵnextContext(3);
128
+ i0.ɵɵadvance(3);
129
+ i0.ɵɵproperty("ngIf", ctx_r2.isShared(collection_r8));
130
+ i0.ɵɵadvance(3);
131
+ i0.ɵɵtextInterpolate(collection_r8.Name);
132
+ i0.ɵɵadvance();
133
+ i0.ɵɵproperty("ngIf", ctx_r2.isShared(collection_r8) && collection_r8.Owner);
134
+ i0.ɵɵadvance();
135
+ i0.ɵɵproperty("ngIf", collection_r8.Description);
136
+ i0.ɵɵadvance(2);
137
+ i0.ɵɵproperty("ngIf", ctx_r2.canShare(collection_r8));
138
+ i0.ɵɵadvance();
139
+ i0.ɵɵproperty("ngIf", ctx_r2.canEdit(collection_r8));
100
140
  i0.ɵɵadvance();
101
- i0.ɵɵproperty("ngIf", collection_r7.Description);
141
+ i0.ɵɵproperty("ngIf", ctx_r2.canDelete(collection_r8));
102
142
  } }
103
- function CollectionsFullViewComponent_div_21_div_1_Template(rf, ctx) { if (rf & 1) {
104
- i0.ɵɵelementStart(0, "div", 30)(1, "div", 31)(2, "h3");
105
- i0.ɵɵtext(3, "Collections");
143
+ function CollectionsFullViewComponent_div_14_div_8_Template(rf, ctx) { if (rf & 1) {
144
+ i0.ɵɵelementStart(0, "div", 33);
145
+ i0.ɵɵtemplate(1, CollectionsFullViewComponent_div_14_div_8_div_1_Template, 13, 7, "div", 34);
106
146
  i0.ɵɵelementEnd();
107
- i0.ɵɵelementStart(4, "span", 32);
108
- i0.ɵɵtext(5);
109
- i0.ɵɵelementEnd()();
110
- i0.ɵɵelementStart(6, "div", 33);
111
- i0.ɵɵtemplate(7, CollectionsFullViewComponent_div_21_div_1_div_7_Template, 12, 2, "div", 34);
112
- i0.ɵɵelementEnd()();
113
147
  } if (rf & 2) {
114
148
  const ctx_r2 = i0.ɵɵnextContext(2);
115
- i0.ɵɵadvance(5);
116
- i0.ɵɵtextInterpolate(ctx_r2.filteredCollections.length);
117
- i0.ɵɵadvance(2);
149
+ i0.ɵɵadvance();
118
150
  i0.ɵɵproperty("ngForOf", ctx_r2.filteredCollections);
119
151
  } }
120
- function CollectionsFullViewComponent_div_21_div_2_button_6_Template(rf, ctx) { if (rf & 1) {
121
- const _r8 = i0.ɵɵgetCurrentView();
122
- i0.ɵɵelementStart(0, "button", 50);
123
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_2_button_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.addArtifact()); });
124
- i0.ɵɵelement(1, "i", 51);
152
+ function CollectionsFullViewComponent_div_14_div_9_button_6_Template(rf, ctx) { if (rf & 1) {
153
+ const _r12 = i0.ɵɵgetCurrentView();
154
+ i0.ɵɵelementStart(0, "button", 31);
155
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_9_button_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.addArtifact()); });
156
+ i0.ɵɵelement(1, "i", 32);
125
157
  i0.ɵɵtext(2, " Add Artifact ");
126
158
  i0.ɵɵelementEnd();
127
159
  } }
128
- function CollectionsFullViewComponent_div_21_div_2_div_8_button_10_Template(rf, ctx) { if (rf & 1) {
129
- const _r11 = i0.ɵɵgetCurrentView();
130
- i0.ɵɵelementStart(0, "button", 58);
131
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_2_div_8_button_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r11); const artifact_r10 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.removeArtifact(artifact_r10)); });
132
- i0.ɵɵelement(1, "i", 59);
160
+ function CollectionsFullViewComponent_div_14_div_9_div_7_div_1_button_10_Template(rf, ctx) { if (rf & 1) {
161
+ const _r15 = i0.ɵɵgetCurrentView();
162
+ i0.ɵɵelementStart(0, "button", 67);
163
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_9_div_7_div_1_button_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r15); const artifact_r14 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.removeArtifact(artifact_r14)); });
164
+ i0.ɵɵelement(1, "i", 68);
133
165
  i0.ɵɵelementEnd();
134
166
  } }
135
- function CollectionsFullViewComponent_div_21_div_2_div_8_Template(rf, ctx) { if (rf & 1) {
136
- const _r9 = i0.ɵɵgetCurrentView();
137
- i0.ɵɵelementStart(0, "div", 52);
138
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_2_div_8_Template_div_click_0_listener() { const artifact_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.viewArtifact(artifact_r10)); });
139
- i0.ɵɵelementStart(1, "div", 53);
140
- i0.ɵɵelement(2, "i", 54);
167
+ function CollectionsFullViewComponent_div_14_div_9_div_7_div_1_Template(rf, ctx) { if (rf & 1) {
168
+ const _r13 = i0.ɵɵgetCurrentView();
169
+ i0.ɵɵelementStart(0, "div", 61);
170
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_9_div_7_div_1_Template_div_click_0_listener() { const artifact_r14 = i0.ɵɵrestoreView(_r13).$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.viewArtifact(artifact_r14)); });
171
+ i0.ɵɵelementStart(1, "div", 62);
172
+ i0.ɵɵelement(2, "i", 63);
141
173
  i0.ɵɵelementEnd();
142
- i0.ɵɵelementStart(3, "div", 38)(4, "div", 39);
174
+ i0.ɵɵelementStart(3, "div", 39)(4, "div", 40);
143
175
  i0.ɵɵtext(5);
144
176
  i0.ɵɵelementEnd();
145
- i0.ɵɵelementStart(6, "div", 55)(7, "span", 56);
177
+ i0.ɵɵelementStart(6, "div", 64)(7, "span", 65);
146
178
  i0.ɵɵtext(8);
147
179
  i0.ɵɵelementEnd()()();
148
- i0.ɵɵelementStart(9, "div", 41);
149
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_21_div_2_div_8_Template_div_click_9_listener($event) { i0.ɵɵrestoreView(_r9); return i0.ɵɵresetView($event.stopPropagation()); });
150
- i0.ɵɵtemplate(10, CollectionsFullViewComponent_div_21_div_2_div_8_button_10_Template, 2, 0, "button", 57);
180
+ i0.ɵɵelementStart(9, "div", 43);
181
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_div_14_div_9_div_7_div_1_Template_div_click_9_listener($event) { i0.ɵɵrestoreView(_r13); return i0.ɵɵresetView($event.stopPropagation()); });
182
+ i0.ɵɵtemplate(10, CollectionsFullViewComponent_div_14_div_9_div_7_div_1_button_10_Template, 2, 0, "button", 66);
151
183
  i0.ɵɵelementEnd()();
152
184
  } if (rf & 2) {
153
- const artifact_r10 = ctx.$implicit;
154
- const ctx_r2 = i0.ɵɵnextContext(3);
185
+ const artifact_r14 = ctx.$implicit;
186
+ const ctx_r2 = i0.ɵɵnextContext(4);
155
187
  i0.ɵɵadvance(5);
156
- i0.ɵɵtextInterpolate(artifact_r10.Name);
188
+ i0.ɵɵtextInterpolate(artifact_r14.Name);
157
189
  i0.ɵɵadvance(3);
158
- i0.ɵɵtextInterpolate(artifact_r10.Type || "Unknown");
190
+ i0.ɵɵtextInterpolate(artifact_r14.Type || "Unknown");
159
191
  i0.ɵɵadvance(2);
160
- i0.ɵɵproperty("ngIf", ctx_r2.currentCollectionId);
192
+ i0.ɵɵproperty("ngIf", ctx_r2.canDeleteCurrent());
161
193
  } }
162
- function CollectionsFullViewComponent_div_21_div_2_Template(rf, ctx) { if (rf & 1) {
163
- i0.ɵɵelementStart(0, "div", 30)(1, "div", 31)(2, "h3");
194
+ function CollectionsFullViewComponent_div_14_div_9_div_7_Template(rf, ctx) { if (rf & 1) {
195
+ i0.ɵɵelementStart(0, "div", 59);
196
+ i0.ɵɵtemplate(1, CollectionsFullViewComponent_div_14_div_9_div_7_div_1_Template, 11, 3, "div", 60);
197
+ i0.ɵɵelementEnd();
198
+ } if (rf & 2) {
199
+ const ctx_r2 = i0.ɵɵnextContext(3);
200
+ i0.ɵɵadvance();
201
+ i0.ɵɵproperty("ngForOf", ctx_r2.filteredArtifacts);
202
+ } }
203
+ function CollectionsFullViewComponent_div_14_div_9_Template(rf, ctx) { if (rf & 1) {
204
+ i0.ɵɵelementStart(0, "div", 25)(1, "div", 26)(2, "h3");
164
205
  i0.ɵɵtext(3, "Artifacts");
165
206
  i0.ɵɵelementEnd();
166
- i0.ɵɵelementStart(4, "span", 32);
207
+ i0.ɵɵelementStart(4, "span", 27);
167
208
  i0.ɵɵtext(5);
168
209
  i0.ɵɵelementEnd();
169
- i0.ɵɵtemplate(6, CollectionsFullViewComponent_div_21_div_2_button_6_Template, 3, 0, "button", 47);
210
+ i0.ɵɵtemplate(6, CollectionsFullViewComponent_div_14_div_9_button_6_Template, 3, 0, "button", 28);
211
+ i0.ɵɵelementEnd();
212
+ i0.ɵɵtemplate(7, CollectionsFullViewComponent_div_14_div_9_div_7_Template, 2, 1, "div", 58);
170
213
  i0.ɵɵelementEnd();
171
- i0.ɵɵelementStart(7, "div", 48);
172
- i0.ɵɵtemplate(8, CollectionsFullViewComponent_div_21_div_2_div_8_Template, 11, 3, "div", 49);
173
- i0.ɵɵelementEnd()();
174
214
  } if (rf & 2) {
175
215
  const ctx_r2 = i0.ɵɵnextContext(2);
176
216
  i0.ɵɵadvance(5);
177
217
  i0.ɵɵtextInterpolate(ctx_r2.filteredArtifacts.length);
178
218
  i0.ɵɵadvance();
179
- i0.ɵɵproperty("ngIf", ctx_r2.currentCollectionId);
180
- i0.ɵɵadvance(2);
181
- i0.ɵɵproperty("ngForOf", ctx_r2.filteredArtifacts);
219
+ i0.ɵɵproperty("ngIf", ctx_r2.canEditCurrent());
220
+ i0.ɵɵadvance();
221
+ i0.ɵɵproperty("ngIf", ctx_r2.filteredArtifacts.length > 0);
182
222
  } }
183
- function CollectionsFullViewComponent_div_21_Template(rf, ctx) { if (rf & 1) {
184
- i0.ɵɵelementStart(0, "div", 28);
185
- i0.ɵɵtemplate(1, CollectionsFullViewComponent_div_21_div_1_Template, 8, 2, "div", 29)(2, CollectionsFullViewComponent_div_21_div_2_Template, 9, 3, "div", 29);
223
+ function CollectionsFullViewComponent_div_14_Template(rf, ctx) { if (rf & 1) {
224
+ i0.ɵɵelementStart(0, "div", 24)(1, "div", 25)(2, "div", 26)(3, "h3");
225
+ i0.ɵɵtext(4, "Collections");
226
+ i0.ɵɵelementEnd();
227
+ i0.ɵɵelementStart(5, "span", 27);
228
+ i0.ɵɵtext(6);
229
+ i0.ɵɵelementEnd();
230
+ i0.ɵɵtemplate(7, CollectionsFullViewComponent_div_14_button_7_Template, 3, 0, "button", 28);
231
+ i0.ɵɵelementEnd();
232
+ i0.ɵɵtemplate(8, CollectionsFullViewComponent_div_14_div_8_Template, 2, 1, "div", 29);
233
+ i0.ɵɵelementEnd();
234
+ i0.ɵɵtemplate(9, CollectionsFullViewComponent_div_14_div_9_Template, 8, 3, "div", 30);
186
235
  i0.ɵɵelementEnd();
187
236
  } if (rf & 2) {
188
237
  const ctx_r2 = i0.ɵɵnextContext();
238
+ i0.ɵɵadvance(6);
239
+ i0.ɵɵtextInterpolate(ctx_r2.filteredCollections.length);
240
+ i0.ɵɵadvance();
241
+ i0.ɵɵproperty("ngIf", ctx_r2.canEditCurrent());
189
242
  i0.ɵɵadvance();
190
243
  i0.ɵɵproperty("ngIf", ctx_r2.filteredCollections.length > 0);
191
244
  i0.ɵɵadvance();
192
- i0.ɵɵproperty("ngIf", ctx_r2.filteredArtifacts.length > 0);
245
+ i0.ɵɵproperty("ngIf", ctx_r2.currentCollectionId);
193
246
  } }
194
247
  /**
195
248
  * Full-panel Collections view component
@@ -198,32 +251,74 @@ function CollectionsFullViewComponent_div_21_Template(rf, ctx) { if (rf & 1) {
198
251
  export class CollectionsFullViewComponent {
199
252
  dialogService;
200
253
  artifactState;
254
+ collectionState;
255
+ permissionService;
201
256
  environmentId;
202
257
  currentUser;
258
+ collectionNavigated = new EventEmitter();
203
259
  collections = [];
204
260
  artifacts = [];
205
261
  filteredCollections = [];
206
262
  filteredArtifacts = [];
207
- searchQuery = '';
208
263
  isLoading = false;
209
264
  breadcrumbs = [];
210
265
  currentCollectionId = null;
211
266
  currentCollection = null;
212
267
  isFormModalOpen = false;
213
268
  editingCollection;
214
- constructor(dialogService, artifactState) {
269
+ isArtifactModalOpen = false;
270
+ userPermissions = new Map();
271
+ isShareModalOpen = false;
272
+ sharingCollection = null;
273
+ destroy$ = new Subject();
274
+ isNavigatingProgrammatically = false;
275
+ constructor(dialogService, artifactState, collectionState, permissionService) {
215
276
  this.dialogService = dialogService;
216
277
  this.artifactState = artifactState;
278
+ this.collectionState = collectionState;
279
+ this.permissionService = permissionService;
217
280
  }
218
281
  ngOnInit() {
219
282
  this.loadData();
283
+ // Subscribe to collection state changes for deep linking
284
+ this.subscribeToCollectionState();
285
+ }
286
+ ngOnDestroy() {
287
+ this.destroy$.next();
288
+ this.destroy$.complete();
289
+ }
290
+ /**
291
+ * Subscribe to collection state changes for deep linking support
292
+ */
293
+ subscribeToCollectionState() {
294
+ // Watch for external navigation requests (e.g., from search or URL)
295
+ this.collectionState.activeCollectionId$
296
+ .pipe(takeUntil(this.destroy$))
297
+ .subscribe(collectionId => {
298
+ // Ignore state changes that we triggered ourselves
299
+ if (this.isNavigatingProgrammatically) {
300
+ return;
301
+ }
302
+ // Only navigate if the state is different from our current state
303
+ if (collectionId !== this.currentCollectionId) {
304
+ if (collectionId) {
305
+ console.log('📁 Collection state changed, navigating to:', collectionId);
306
+ this.navigateToCollectionById(collectionId);
307
+ }
308
+ else {
309
+ console.log('📁 Collection state cleared, navigating to root');
310
+ this.navigateToRoot();
311
+ }
312
+ }
313
+ });
220
314
  }
221
315
  async loadData() {
222
316
  this.isLoading = true;
223
317
  try {
224
318
  await Promise.all([
225
319
  this.loadCollections(),
226
- this.loadArtifacts()
320
+ this.loadArtifacts(),
321
+ this.loadCurrentCollectionPermission()
227
322
  ]);
228
323
  }
229
324
  finally {
@@ -233,8 +328,16 @@ export class CollectionsFullViewComponent {
233
328
  async loadCollections() {
234
329
  try {
235
330
  const rv = new RunView();
236
- const filter = `EnvironmentID='${this.environmentId}'` +
331
+ // Load collections where user is owner OR has permissions
332
+ const ownerFilter = `OwnerID='${this.currentUser.ID}'`;
333
+ const permissionSubquery = `ID IN (
334
+ SELECT CollectionID
335
+ FROM [__mj].[vwCollectionPermissions]
336
+ WHERE UserID='${this.currentUser.ID}'
337
+ )`;
338
+ const baseFilter = `EnvironmentID='${this.environmentId}'` +
237
339
  (this.currentCollectionId ? ` AND ParentID='${this.currentCollectionId}'` : ' AND ParentID IS NULL');
340
+ const filter = `${baseFilter} AND (OwnerID IS NULL OR ${ownerFilter} OR ${permissionSubquery})`;
238
341
  const result = await rv.RunView({
239
342
  EntityName: 'MJ: Collections',
240
343
  ExtraFilter: filter,
@@ -244,13 +347,32 @@ export class CollectionsFullViewComponent {
244
347
  }, this.currentUser);
245
348
  if (result.Success) {
246
349
  this.collections = result.Results || [];
247
- this.applySearch();
350
+ await this.loadUserPermissions();
351
+ this.filteredCollections = [...this.collections];
248
352
  }
249
353
  }
250
354
  catch (error) {
251
355
  console.error('Failed to load collections:', error);
252
356
  }
253
357
  }
358
+ async loadUserPermissions() {
359
+ this.userPermissions.clear();
360
+ for (const collection of this.collections) {
361
+ const permission = await this.permissionService.checkPermission(collection.ID, this.currentUser.ID, this.currentUser);
362
+ if (permission) {
363
+ this.userPermissions.set(collection.ID, permission);
364
+ }
365
+ }
366
+ }
367
+ async loadCurrentCollectionPermission() {
368
+ if (!this.currentCollectionId || !this.currentCollection) {
369
+ return;
370
+ }
371
+ const permission = await this.permissionService.checkPermission(this.currentCollectionId, this.currentUser.ID, this.currentUser);
372
+ if (permission) {
373
+ this.userPermissions.set(this.currentCollectionId, permission);
374
+ }
375
+ }
254
376
  async loadArtifacts() {
255
377
  if (!this.currentCollectionId) {
256
378
  this.artifacts = [];
@@ -259,70 +381,167 @@ export class CollectionsFullViewComponent {
259
381
  }
260
382
  try {
261
383
  this.artifacts = await this.artifactState.loadArtifactsForCollection(this.currentCollectionId, this.currentUser);
262
- this.applySearch();
384
+ this.filteredArtifacts = [...this.artifacts];
263
385
  }
264
386
  catch (error) {
265
387
  console.error('Failed to load artifacts:', error);
266
388
  }
267
389
  }
268
- onSearchChange(query) {
269
- this.applySearch();
390
+ async openCollection(collection) {
391
+ this.isNavigatingProgrammatically = true;
392
+ try {
393
+ this.breadcrumbs.push({ id: collection.ID, name: collection.Name });
394
+ this.currentCollectionId = collection.ID;
395
+ this.currentCollection = collection;
396
+ await this.loadData();
397
+ // Update state service
398
+ this.collectionState.setActiveCollection(collection.ID);
399
+ // Emit navigation event
400
+ this.collectionNavigated.emit({
401
+ collectionId: collection.ID,
402
+ artifactId: null
403
+ });
404
+ }
405
+ finally {
406
+ this.isNavigatingProgrammatically = false;
407
+ }
270
408
  }
271
- applySearch() {
272
- if (!this.searchQuery.trim()) {
273
- this.filteredCollections = [...this.collections];
274
- this.filteredArtifacts = [...this.artifacts];
409
+ async navigateTo(crumb) {
410
+ this.isNavigatingProgrammatically = true;
411
+ try {
412
+ const index = this.breadcrumbs.findIndex(b => b.id === crumb.id);
413
+ if (index !== -1) {
414
+ this.breadcrumbs = this.breadcrumbs.slice(0, index + 1);
415
+ this.currentCollectionId = crumb.id;
416
+ // Load the collection entity
417
+ const md = new Metadata();
418
+ this.currentCollection = await md.GetEntityObject('MJ: Collections', this.currentUser);
419
+ await this.currentCollection.Load(crumb.id);
420
+ await this.loadData();
421
+ // Update state service
422
+ this.collectionState.setActiveCollection(crumb.id);
423
+ // Emit navigation event
424
+ this.collectionNavigated.emit({
425
+ collectionId: crumb.id,
426
+ artifactId: null
427
+ });
428
+ }
275
429
  }
276
- else {
277
- const query = this.searchQuery.toLowerCase();
278
- this.filteredCollections = this.collections.filter(c => c.Name.toLowerCase().includes(query) ||
279
- (c.Description && c.Description.toLowerCase().includes(query)));
280
- this.filteredArtifacts = this.artifacts.filter(a => a.Name.toLowerCase().includes(query));
430
+ finally {
431
+ this.isNavigatingProgrammatically = false;
281
432
  }
282
433
  }
283
- async openCollection(collection) {
284
- this.breadcrumbs.push({ id: collection.ID, name: collection.Name });
285
- this.currentCollectionId = collection.ID;
286
- this.currentCollection = collection;
287
- this.searchQuery = '';
288
- await this.loadData();
434
+ async navigateToRoot() {
435
+ this.isNavigatingProgrammatically = true;
436
+ try {
437
+ this.breadcrumbs = [];
438
+ this.currentCollectionId = null;
439
+ this.currentCollection = null;
440
+ await this.loadData();
441
+ // Update state service
442
+ this.collectionState.setActiveCollection(null);
443
+ // Emit navigation event
444
+ this.collectionNavigated.emit({
445
+ collectionId: null,
446
+ artifactId: null
447
+ });
448
+ }
449
+ finally {
450
+ this.isNavigatingProgrammatically = false;
451
+ }
289
452
  }
290
- async navigateTo(crumb) {
291
- const index = this.breadcrumbs.findIndex(b => b.id === crumb.id);
292
- if (index !== -1) {
293
- this.breadcrumbs = this.breadcrumbs.slice(0, index + 1);
294
- this.currentCollectionId = crumb.id;
295
- // Load the collection entity
453
+ /**
454
+ * Navigate to a collection by ID, building the breadcrumb trail
455
+ * Used for deep linking from search results or URL parameters
456
+ */
457
+ async navigateToCollectionById(collectionId) {
458
+ this.isNavigatingProgrammatically = true;
459
+ try {
460
+ console.log('📁 Navigating to collection by ID:', collectionId);
461
+ // Load the target collection
296
462
  const md = new Metadata();
297
- this.currentCollection = await md.GetEntityObject('MJ: Collections', this.currentUser);
298
- await this.currentCollection.Load(crumb.id);
299
- this.searchQuery = '';
463
+ const targetCollection = await md.GetEntityObject('MJ: Collections', this.currentUser);
464
+ await targetCollection.Load(collectionId);
465
+ if (!targetCollection || !targetCollection.ID) {
466
+ console.error('❌ Failed to load collection:', collectionId);
467
+ return;
468
+ }
469
+ // Build breadcrumb trail by traversing parent hierarchy
470
+ // Note: breadcrumbs includes ALL collections in the path including the current one
471
+ const trail = [];
472
+ let currentId = targetCollection.ParentID;
473
+ while (currentId) {
474
+ const parentCollection = await md.GetEntityObject('MJ: Collections', this.currentUser);
475
+ await parentCollection.Load(currentId);
476
+ if (parentCollection && parentCollection.ID) {
477
+ // Add to front of trail (we're working backwards)
478
+ trail.unshift({
479
+ id: parentCollection.ID,
480
+ name: parentCollection.Name
481
+ });
482
+ currentId = parentCollection.ParentID;
483
+ }
484
+ else {
485
+ break;
486
+ }
487
+ }
488
+ // Add the target collection to the trail (breadcrumbs includes current collection)
489
+ trail.push({
490
+ id: targetCollection.ID,
491
+ name: targetCollection.Name
492
+ });
493
+ // Update component state
494
+ this.breadcrumbs = trail;
495
+ this.currentCollectionId = targetCollection.ID;
496
+ this.currentCollection = targetCollection;
497
+ // Load collections and artifacts for this collection
300
498
  await this.loadData();
499
+ // Update state service
500
+ this.collectionState.setActiveCollection(targetCollection.ID);
501
+ // Emit navigation event
502
+ // NOTE: We don't emit artifactId here because this is for deep linking/programmatic navigation
503
+ // Artifact state is managed separately by the artifact state service
504
+ this.collectionNavigated.emit({
505
+ collectionId: targetCollection.ID
506
+ });
507
+ console.log('✅ Successfully navigated to collection with breadcrumb trail:', trail);
508
+ }
509
+ catch (error) {
510
+ console.error('❌ Error navigating to collection:', error);
511
+ }
512
+ finally {
513
+ this.isNavigatingProgrammatically = false;
301
514
  }
302
- }
303
- async navigateToRoot() {
304
- this.breadcrumbs = [];
305
- this.currentCollectionId = null;
306
- this.currentCollection = null;
307
- this.searchQuery = '';
308
- await this.loadData();
309
515
  }
310
516
  refresh() {
311
517
  this.loadData();
312
518
  }
313
- createCollection() {
519
+ async createCollection() {
520
+ // Validate user can edit current collection (or at root level)
521
+ if (this.currentCollection) {
522
+ const canEdit = await this.validatePermission(this.currentCollection, 'edit');
523
+ if (!canEdit)
524
+ return;
525
+ }
314
526
  this.editingCollection = undefined;
315
527
  this.isFormModalOpen = true;
316
528
  }
317
- editCollection(collection) {
529
+ async editCollection(collection) {
530
+ const canEdit = await this.validatePermission(collection, 'edit');
531
+ if (!canEdit)
532
+ return;
318
533
  this.editingCollection = collection;
319
534
  this.isFormModalOpen = true;
320
535
  }
321
536
  async deleteCollection(collection) {
322
537
  console.log('deleteCollection called for:', collection.Name, collection.ID);
538
+ // Validate user has delete permission
539
+ const canDelete = await this.validatePermission(collection, 'delete');
540
+ if (!canDelete)
541
+ return;
323
542
  const confirmed = await this.dialogService.confirm({
324
543
  title: 'Delete Collection',
325
- message: `Are you sure you want to delete "${collection.Name}"? This action cannot be undone.`,
544
+ message: `Are you sure you want to delete "${collection.Name}"? This will also delete all child collections and remove all artifacts. This action cannot be undone.`,
326
545
  okText: 'Delete',
327
546
  cancelText: 'Cancel',
328
547
  dangerous: true
@@ -330,15 +549,9 @@ export class CollectionsFullViewComponent {
330
549
  console.log('Delete confirmed:', confirmed);
331
550
  if (confirmed) {
332
551
  try {
333
- console.log('Attempting to delete collection...');
334
- const deleted = await collection.Delete();
335
- console.log('Delete result:', deleted);
336
- if (deleted) {
337
- await this.loadCollections();
338
- }
339
- else {
340
- await this.dialogService.alert('Error', 'Failed to delete collection.');
341
- }
552
+ console.log('Attempting to delete collection and all children...');
553
+ await this.deleteCollectionRecursive(collection.ID);
554
+ await this.loadCollections();
342
555
  }
343
556
  catch (error) {
344
557
  console.error('Error deleting collection:', error);
@@ -346,22 +559,79 @@ export class CollectionsFullViewComponent {
346
559
  }
347
560
  }
348
561
  }
562
+ async deleteCollectionRecursive(collectionId) {
563
+ const rv = new RunView();
564
+ // Step 1: Find and delete all child collections recursively
565
+ const childrenResult = await rv.RunView({
566
+ EntityName: 'MJ: Collections',
567
+ ExtraFilter: `ParentID='${collectionId}'`,
568
+ MaxRows: 1000,
569
+ ResultType: 'entity_object'
570
+ }, this.currentUser);
571
+ if (childrenResult.Success && childrenResult.Results) {
572
+ for (const child of childrenResult.Results) {
573
+ await this.deleteCollectionRecursive(child.ID);
574
+ }
575
+ }
576
+ // Step 2: Delete all permissions for this collection
577
+ await this.permissionService.deleteAllPermissions(collectionId, this.currentUser);
578
+ // Step 3: Delete all artifact links for this collection
579
+ const artifactsResult = await rv.RunView({
580
+ EntityName: 'MJ: Collection Artifacts',
581
+ ExtraFilter: `CollectionID='${collectionId}'`,
582
+ MaxRows: 1000,
583
+ ResultType: 'entity_object'
584
+ }, this.currentUser);
585
+ if (artifactsResult.Success && artifactsResult.Results) {
586
+ for (const ca of artifactsResult.Results) {
587
+ await ca.Delete();
588
+ }
589
+ }
590
+ // Step 4: Delete the collection itself
591
+ const md = new Metadata();
592
+ const collection = await md.GetEntityObject('MJ: Collections', this.currentUser);
593
+ await collection.Load(collectionId);
594
+ const deleted = await collection.Delete();
595
+ if (!deleted) {
596
+ throw new Error(`Failed to delete collection: ${collection.LatestResult?.Message || 'Unknown error'}`);
597
+ }
598
+ }
349
599
  async onCollectionSaved(collection) {
350
600
  this.isFormModalOpen = false;
351
601
  this.editingCollection = undefined;
352
602
  await this.loadCollections();
603
+ // Reload current collection permission (it was cleared by loadUserPermissions)
604
+ await this.loadCurrentCollectionPermission();
353
605
  }
354
606
  onFormCancelled() {
355
607
  this.isFormModalOpen = false;
356
608
  this.editingCollection = undefined;
357
609
  }
358
610
  async addArtifact() {
359
- // Will be implemented with artifact upload modal
360
- await this.dialogService.alert('Coming Soon', 'Artifact upload functionality will be available soon.');
611
+ // Validate user can edit current collection
612
+ if (this.currentCollection) {
613
+ const canEdit = await this.validatePermission(this.currentCollection, 'edit');
614
+ if (!canEdit)
615
+ return;
616
+ }
617
+ this.isArtifactModalOpen = true;
618
+ }
619
+ async onArtifactSaved(artifact) {
620
+ this.isArtifactModalOpen = false;
621
+ await this.loadArtifacts();
622
+ }
623
+ onArtifactModalCancelled() {
624
+ this.isArtifactModalOpen = false;
361
625
  }
362
626
  async removeArtifact(artifact) {
363
627
  if (!this.currentCollectionId)
364
628
  return;
629
+ // Validate user has delete permission on current collection
630
+ if (this.currentCollection) {
631
+ const canDelete = await this.validatePermission(this.currentCollection, 'delete');
632
+ if (!canDelete)
633
+ return;
634
+ }
365
635
  const confirmed = await this.dialogService.confirm({
366
636
  title: 'Remove Artifact',
367
637
  message: `Remove "${artifact.Name}" from this collection?`,
@@ -381,9 +651,94 @@ export class CollectionsFullViewComponent {
381
651
  }
382
652
  viewArtifact(artifact) {
383
653
  this.artifactState.openArtifact(artifact.ID);
654
+ // Emit navigation event with both collection and artifact
655
+ this.collectionNavigated.emit({
656
+ collectionId: this.currentCollectionId,
657
+ artifactId: artifact.ID
658
+ });
659
+ }
660
+ // Permission validation and checking methods
661
+ async validatePermission(collection, requiredPermission) {
662
+ // Owner has all permissions (including backwards compatibility for null OwnerID)
663
+ if (!collection?.OwnerID || collection.OwnerID === this.currentUser.ID) {
664
+ return true;
665
+ }
666
+ const permission = this.userPermissions.get(collection.ID);
667
+ if (!permission) {
668
+ await this.dialogService.alert('Permission Denied', 'You do not have permission to perform this action.');
669
+ return false;
670
+ }
671
+ const hasPermission = (requiredPermission === 'edit' && permission.canEdit) ||
672
+ (requiredPermission === 'delete' && permission.canDelete) ||
673
+ (requiredPermission === 'share' && permission.canShare);
674
+ if (!hasPermission) {
675
+ const permissionName = requiredPermission.charAt(0).toUpperCase() + requiredPermission.slice(1);
676
+ await this.dialogService.alert('Permission Denied', `You do not have ${permissionName} permission for this collection.`);
677
+ return false;
678
+ }
679
+ return true;
680
+ }
681
+ canEdit(collection) {
682
+ // Backwards compatibility: treat null OwnerID as owned by current user
683
+ if (!collection.OwnerID || collection.OwnerID === this.currentUser.ID)
684
+ return true;
685
+ // Check permission record
686
+ const permission = this.userPermissions.get(collection.ID);
687
+ return permission?.canEdit || false;
688
+ }
689
+ canDelete(collection) {
690
+ // Backwards compatibility: treat null OwnerID as owned by current user
691
+ if (!collection.OwnerID || collection.OwnerID === this.currentUser.ID)
692
+ return true;
693
+ // Check permission record
694
+ const permission = this.userPermissions.get(collection.ID);
695
+ return permission?.canDelete || false;
696
+ }
697
+ canShare(collection) {
698
+ // Backwards compatibility: treat null OwnerID as owned by current user
699
+ if (!collection.OwnerID || collection.OwnerID === this.currentUser.ID)
700
+ return true;
701
+ // Check permission record
702
+ const permission = this.userPermissions.get(collection.ID);
703
+ return permission?.canShare || false;
704
+ }
705
+ canEditCurrent() {
706
+ // At root level, anyone can create
707
+ if (!this.currentCollectionId || !this.currentCollection) {
708
+ return true;
709
+ }
710
+ return this.canEdit(this.currentCollection);
384
711
  }
385
- static ɵfac = function CollectionsFullViewComponent_Factory(t) { return new (t || CollectionsFullViewComponent)(i0.ɵɵdirectiveInject(i1.DialogService), i0.ɵɵdirectiveInject(i2.ArtifactStateService)); };
386
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CollectionsFullViewComponent, selectors: [["mj-collections-full-view"]], inputs: { environmentId: "environmentId", currentUser: "currentUser" }, decls: 23, vars: 11, consts: [[1, "collections-view"], [1, "collections-header"], [1, "collections-breadcrumb"], [1, "breadcrumb-item"], [1, "fas", "fa-home"], [1, "breadcrumb-link", 3, "click"], ["class", "breadcrumb-path", 4, "ngIf"], [1, "collections-search"], [1, "fas", "fa-search"], ["type", "text", "placeholder", "Search collections and artifacts...", 1, "collection-search-input", 3, "ngModelChange", "ngModel"], [1, "collections-actions"], ["title", "New Collection", 1, "btn-primary", 3, "click"], [1, "fas", "fa-folder-plus"], ["title", "Refresh", 1, "btn-secondary", 3, "click"], [1, "fas", "fa-sync"], [1, "collections-content"], ["class", "loading-state", 4, "ngIf"], ["class", "empty-state", 4, "ngIf"], ["class", "content-grid", 4, "ngIf"], [3, "saved", "cancelled", "isOpen", "collection", "parentCollection", "environmentId", "currentUser"], [1, "breadcrumb-path"], [4, "ngFor", "ngForOf"], [1, "fas", "fa-chevron-right", "breadcrumb-separator"], [1, "loading-state"], [1, "fas", "fa-spinner", "fa-spin"], [1, "empty-state"], [1, "fas", "fa-folder-open"], [1, "btn-primary", 3, "click"], [1, "content-grid"], ["class", "section", 4, "ngIf"], [1, "section"], [1, "section-header"], [1, "section-count"], [1, "collection-grid"], ["class", "collection-card", 3, "click", 4, "ngFor", "ngForOf"], [1, "collection-card", 3, "click"], [1, "card-icon"], [1, "fas", "fa-folder"], [1, "card-content"], [1, "card-name"], ["class", "card-description", 4, "ngIf"], [1, "card-actions", 3, "click"], ["title", "Edit", 1, "card-action-btn", 3, "click"], [1, "fas", "fa-edit"], ["title", "Delete", 1, "card-action-btn", "danger", 3, "click"], [1, "fas", "fa-trash"], [1, "card-description"], ["class", "btn-secondary btn-sm", 3, "click", 4, "ngIf"], [1, "artifact-grid"], ["class", "artifact-card", 3, "click", 4, "ngFor", "ngForOf"], [1, "btn-secondary", "btn-sm", 3, "click"], [1, "fas", "fa-plus"], [1, "artifact-card", 3, "click"], [1, "card-icon", "artifact-icon"], [1, "fas", "fa-file-alt"], [1, "card-meta"], [1, "artifact-type"], ["class", "card-action-btn", "title", "Remove from collection", 3, "click", 4, "ngIf"], ["title", "Remove from collection", 1, "card-action-btn", 3, "click"], [1, "fas", "fa-times"]], template: function CollectionsFullViewComponent_Template(rf, ctx) { if (rf & 1) {
712
+ canDeleteCurrent() {
713
+ // At root level, no delete needed
714
+ if (!this.currentCollectionId || !this.currentCollection) {
715
+ return false;
716
+ }
717
+ return this.canDelete(this.currentCollection);
718
+ }
719
+ isShared(collection) {
720
+ // Collection is shared if user is not the owner and OwnerID is set
721
+ return collection.OwnerID != null && collection.OwnerID !== this.currentUser.ID;
722
+ }
723
+ // Sharing methods
724
+ async shareCollection(collection) {
725
+ // Validate user has share permission
726
+ const canShare = await this.validatePermission(collection, 'share');
727
+ if (!canShare)
728
+ return;
729
+ this.sharingCollection = collection;
730
+ this.isShareModalOpen = true;
731
+ }
732
+ async onPermissionsChanged() {
733
+ // Reload collections and permissions after sharing changes
734
+ await this.loadCollections();
735
+ }
736
+ onShareModalCancelled() {
737
+ this.isShareModalOpen = false;
738
+ this.sharingCollection = null;
739
+ }
740
+ static ɵfac = function CollectionsFullViewComponent_Factory(t) { return new (t || CollectionsFullViewComponent)(i0.ɵɵdirectiveInject(i1.DialogService), i0.ɵɵdirectiveInject(i2.ArtifactStateService), i0.ɵɵdirectiveInject(i3.CollectionStateService), i0.ɵɵdirectiveInject(i4.CollectionPermissionService)); };
741
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CollectionsFullViewComponent, selectors: [["mj-collections-full-view"]], inputs: { environmentId: "environmentId", currentUser: "currentUser" }, outputs: { collectionNavigated: "collectionNavigated" }, decls: 18, vars: 17, consts: [[1, "collections-view"], [1, "collections-header"], [1, "collections-breadcrumb"], [1, "breadcrumb-item"], [1, "fas", "fa-home"], [1, "breadcrumb-link", 3, "click"], ["class", "breadcrumb-path", 4, "ngIf"], [1, "collections-actions"], ["class", "btn-primary", "title", "New Collection", 3, "click", 4, "ngIf"], ["title", "Refresh", 1, "btn-secondary", 3, "click"], [1, "fas", "fa-sync"], [1, "collections-content"], ["class", "loading-state", 4, "ngIf"], ["class", "content-grid", 4, "ngIf"], [3, "saved", "cancelled", "isOpen", "collection", "parentCollection", "environmentId", "currentUser"], [3, "saved", "cancelled", "isOpen", "collectionId", "environmentId", "currentUser"], [3, "saved", "cancelled", "isOpen", "collection", "currentUser", "currentUserPermissions"], [1, "breadcrumb-path"], [4, "ngFor", "ngForOf"], [1, "fas", "fa-chevron-right", "breadcrumb-separator"], ["title", "New Collection", 1, "btn-primary", 3, "click"], [1, "fas", "fa-folder-plus"], [1, "loading-state"], [1, "fas", "fa-spinner", "fa-spin"], [1, "content-grid"], [1, "section"], [1, "section-header"], [1, "section-count"], ["class", "btn-primary btn-sm", 3, "click", 4, "ngIf"], ["class", "collection-grid", 4, "ngIf"], ["class", "section", 4, "ngIf"], [1, "btn-primary", "btn-sm", 3, "click"], [1, "fas", "fa-plus"], [1, "collection-grid"], ["class", "collection-card", 3, "click", 4, "ngFor", "ngForOf"], [1, "collection-card", 3, "click"], [1, "card-icon"], [1, "fas", "fa-folder"], ["class", "shared-badge", "title", "Shared with you", 4, "ngIf"], [1, "card-content"], [1, "card-name"], ["class", "card-owner", 4, "ngIf"], ["class", "card-description", 4, "ngIf"], [1, "card-actions", 3, "click"], ["class", "card-action-btn", "title", "Share", 3, "click", 4, "ngIf"], ["class", "card-action-btn", "title", "Edit", 3, "click", 4, "ngIf"], ["class", "card-action-btn danger", "title", "Delete", 3, "click", 4, "ngIf"], ["title", "Shared with you", 1, "shared-badge"], [1, "fas", "fa-users"], [1, "card-owner"], [1, "fas", "fa-user"], [1, "card-description"], ["title", "Share", 1, "card-action-btn", 3, "click"], [1, "fas", "fa-share-alt"], ["title", "Edit", 1, "card-action-btn", 3, "click"], [1, "fas", "fa-edit"], ["title", "Delete", 1, "card-action-btn", "danger", 3, "click"], [1, "fas", "fa-trash"], ["class", "artifact-grid", 4, "ngIf"], [1, "artifact-grid"], ["class", "artifact-card", 3, "click", 4, "ngFor", "ngForOf"], [1, "artifact-card", 3, "click"], [1, "card-icon", "artifact-icon"], [1, "fas", "fa-file-alt"], [1, "card-meta"], [1, "artifact-type"], ["class", "card-action-btn", "title", "Remove from collection", 3, "click", 4, "ngIf"], ["title", "Remove from collection", 1, "card-action-btn", 3, "click"], [1, "fas", "fa-times"]], template: function CollectionsFullViewComponent_Template(rf, ctx) { if (rf & 1) {
387
742
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3);
388
743
  i0.ɵɵelement(4, "i", 4);
389
744
  i0.ɵɵelementStart(5, "a", 5);
@@ -393,42 +748,39 @@ export class CollectionsFullViewComponent {
393
748
  i0.ɵɵtemplate(7, CollectionsFullViewComponent_span_7_Template, 2, 1, "span", 6);
394
749
  i0.ɵɵelementEnd();
395
750
  i0.ɵɵelementStart(8, "div", 7);
396
- i0.ɵɵelement(9, "i", 8);
397
- i0.ɵɵelementStart(10, "input", 9);
398
- i0.ɵɵtwoWayListener("ngModelChange", function CollectionsFullViewComponent_Template_input_ngModelChange_10_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.searchQuery, $event) || (ctx.searchQuery = $event); return $event; });
399
- i0.ɵɵlistener("ngModelChange", function CollectionsFullViewComponent_Template_input_ngModelChange_10_listener($event) { return ctx.onSearchChange($event); });
400
- i0.ɵɵelementEnd()();
401
- i0.ɵɵelementStart(11, "div", 10)(12, "button", 11);
402
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_Template_button_click_12_listener() { return ctx.createCollection(); });
403
- i0.ɵɵelement(13, "i", 12);
404
- i0.ɵɵtext(14, " New Collection ");
405
- i0.ɵɵelementEnd();
406
- i0.ɵɵelementStart(15, "button", 13);
407
- i0.ɵɵlistener("click", function CollectionsFullViewComponent_Template_button_click_15_listener() { return ctx.refresh(); });
408
- i0.ɵɵelement(16, "i", 14);
751
+ i0.ɵɵtemplate(9, CollectionsFullViewComponent_button_9_Template, 3, 0, "button", 8);
752
+ i0.ɵɵelementStart(10, "button", 9);
753
+ i0.ɵɵlistener("click", function CollectionsFullViewComponent_Template_button_click_10_listener() { return ctx.refresh(); });
754
+ i0.ɵɵelement(11, "i", 10);
409
755
  i0.ɵɵelementEnd()()();
410
- i0.ɵɵelementStart(17, "div", 15);
411
- i0.ɵɵtemplate(18, CollectionsFullViewComponent_div_18_Template, 4, 0, "div", 16)(19, CollectionsFullViewComponent_div_19_Template, 9, 0, "div", 17)(20, CollectionsFullViewComponent_div_20_Template, 4, 0, "div", 17)(21, CollectionsFullViewComponent_div_21_Template, 3, 2, "div", 18);
756
+ i0.ɵɵelementStart(12, "div", 11);
757
+ i0.ɵɵtemplate(13, CollectionsFullViewComponent_div_13_Template, 4, 0, "div", 12)(14, CollectionsFullViewComponent_div_14_Template, 10, 4, "div", 13);
412
758
  i0.ɵɵelementEnd()();
413
- i0.ɵɵelementStart(22, "mj-collection-form-modal", 19);
414
- i0.ɵɵlistener("saved", function CollectionsFullViewComponent_Template_mj_collection_form_modal_saved_22_listener($event) { return ctx.onCollectionSaved($event); })("cancelled", function CollectionsFullViewComponent_Template_mj_collection_form_modal_cancelled_22_listener() { return ctx.onFormCancelled(); });
759
+ i0.ɵɵelementStart(15, "mj-collection-form-modal", 14);
760
+ i0.ɵɵlistener("saved", function CollectionsFullViewComponent_Template_mj_collection_form_modal_saved_15_listener($event) { return ctx.onCollectionSaved($event); })("cancelled", function CollectionsFullViewComponent_Template_mj_collection_form_modal_cancelled_15_listener() { return ctx.onFormCancelled(); });
761
+ i0.ɵɵelementEnd();
762
+ i0.ɵɵelementStart(16, "mj-artifact-create-modal", 15);
763
+ i0.ɵɵlistener("saved", function CollectionsFullViewComponent_Template_mj_artifact_create_modal_saved_16_listener($event) { return ctx.onArtifactSaved($event); })("cancelled", function CollectionsFullViewComponent_Template_mj_artifact_create_modal_cancelled_16_listener() { return ctx.onArtifactModalCancelled(); });
764
+ i0.ɵɵelementEnd();
765
+ i0.ɵɵelementStart(17, "mj-collection-share-modal", 16);
766
+ i0.ɵɵlistener("saved", function CollectionsFullViewComponent_Template_mj_collection_share_modal_saved_17_listener() { return ctx.onPermissionsChanged(); })("cancelled", function CollectionsFullViewComponent_Template_mj_collection_share_modal_cancelled_17_listener() { return ctx.onShareModalCancelled(); });
415
767
  i0.ɵɵelementEnd();
416
768
  } if (rf & 2) {
417
769
  i0.ɵɵadvance(7);
418
770
  i0.ɵɵproperty("ngIf", ctx.breadcrumbs.length > 0);
419
- i0.ɵɵadvance(3);
420
- i0.ɵɵtwoWayProperty("ngModel", ctx.searchQuery);
421
- i0.ɵɵadvance(8);
771
+ i0.ɵɵadvance(2);
772
+ i0.ɵɵproperty("ngIf", ctx.canEditCurrent());
773
+ i0.ɵɵadvance(4);
422
774
  i0.ɵɵproperty("ngIf", ctx.isLoading);
423
775
  i0.ɵɵadvance();
424
- i0.ɵɵproperty("ngIf", !ctx.isLoading && ctx.collections.length === 0 && ctx.artifacts.length === 0 && !ctx.searchQuery);
776
+ i0.ɵɵproperty("ngIf", !ctx.isLoading);
425
777
  i0.ɵɵadvance();
426
- i0.ɵɵproperty("ngIf", !ctx.isLoading && ctx.collections.length === 0 && ctx.artifacts.length === 0 && ctx.searchQuery);
778
+ i0.ɵɵproperty("isOpen", ctx.isFormModalOpen)("collection", ctx.editingCollection)("parentCollection", ctx.currentCollection || undefined)("environmentId", ctx.environmentId)("currentUser", ctx.currentUser);
427
779
  i0.ɵɵadvance();
428
- i0.ɵɵproperty("ngIf", !ctx.isLoading && (ctx.filteredCollections.length > 0 || ctx.filteredArtifacts.length > 0));
780
+ i0.ɵɵproperty("isOpen", ctx.isArtifactModalOpen)("collectionId", ctx.currentCollectionId || "")("environmentId", ctx.environmentId)("currentUser", ctx.currentUser);
429
781
  i0.ɵɵadvance();
430
- i0.ɵɵproperty("isOpen", ctx.isFormModalOpen)("collection", ctx.editingCollection)("parentCollection", ctx.currentCollection || undefined)("environmentId", ctx.environmentId)("currentUser", ctx.currentUser);
431
- } }, dependencies: [i3.NgForOf, i3.NgIf, i4.DefaultValueAccessor, i4.NgControlStatus, i4.NgModel, i5.CollectionFormModalComponent], styles: [".collections-view[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n }\n\n .collections-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 16px 24px;\n border-bottom: 1px solid #E5E7EB;\n gap: 16px;\n }\n\n .collections-breadcrumb[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .breadcrumb-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .breadcrumb-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #6B7280;\n font-size: 14px;\n }\n\n .breadcrumb-link[_ngcontent-%COMP%] {\n color: #111827;\n font-weight: 500;\n cursor: pointer;\n text-decoration: none;\n white-space: nowrap;\n transition: color 150ms ease;\n }\n\n .breadcrumb-link[_ngcontent-%COMP%]:hover {\n color: #1e40af;\n }\n\n .breadcrumb-link.active[_ngcontent-%COMP%] {\n color: #6B7280;\n cursor: default;\n }\n\n .breadcrumb-path[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n overflow-x: auto;\n }\n\n .breadcrumb-separator[_ngcontent-%COMP%] {\n color: #D1D5DB;\n font-size: 10px;\n }\n\n .collections-search[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n background: #F9FAFB;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n padding: 8px 12px;\n min-width: 300px;\n }\n\n .collections-search[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #9CA3AF;\n font-size: 14px;\n }\n\n .collection-search-input[_ngcontent-%COMP%] {\n border: none;\n background: transparent;\n outline: none;\n font-size: 14px;\n flex: 1;\n color: #111827;\n }\n\n .collection-search-input[_ngcontent-%COMP%]::placeholder {\n color: #9CA3AF;\n }\n\n .collections-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n }\n\n .btn-primary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #1e40af;\n border: none;\n border-radius: 6px;\n color: white;\n font-size: 14px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n\n .btn-primary[_ngcontent-%COMP%]:hover {\n background: #1e3a8a;\n }\n\n .btn-secondary[_ngcontent-%COMP%] {\n padding: 8px 12px;\n background: transparent;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n cursor: pointer;\n color: #6B7280;\n transition: all 150ms ease;\n }\n\n .btn-secondary[_ngcontent-%COMP%]:hover {\n background: #F9FAFB;\n color: #111827;\n }\n\n .btn-sm[_ngcontent-%COMP%] {\n padding: 6px 12px;\n font-size: 13px;\n }\n\n .collections-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .loading-state[_ngcontent-%COMP%], .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #9CA3AF;\n max-width: 400px;\n margin: 0 auto;\n text-align: center;\n padding: 48px 24px;\n }\n\n .loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n margin-bottom: 24px;\n opacity: 0.3;\n color: #D1D5DB;\n }\n\n .empty-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: #374151;\n font-size: 20px;\n font-weight: 600;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: #6B7280;\n line-height: 1.5;\n }\n\n .loading-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }\n\n .empty-state[_ngcontent-%COMP%] .btn-primary[_ngcontent-%COMP%] {\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n }\n\n .empty-state[_ngcontent-%COMP%] .btn-primary[_ngcontent-%COMP%]:hover {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n }\n\n .content-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 32px;\n }\n\n .section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #111827;\n }\n\n .section-count[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 8px;\n background: #EFF6FF;\n border-radius: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #1e40af;\n }\n\n .collection-grid[_ngcontent-%COMP%], .artifact-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 16px;\n }\n\n .collection-card[_ngcontent-%COMP%], .artifact-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: start;\n gap: 16px;\n padding: 20px;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 8px;\n cursor: pointer;\n transition: all 150ms ease;\n position: relative;\n }\n\n .collection-card[_ngcontent-%COMP%]:hover, .artifact-card[_ngcontent-%COMP%]:hover {\n border-color: #1e40af;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n }\n\n .card-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #EFF6FF;\n border-radius: 8px;\n flex-shrink: 0;\n }\n\n .card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: #1e40af;\n }\n\n .artifact-icon[_ngcontent-%COMP%] {\n background: #F0FDF4;\n }\n\n .artifact-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #059669;\n }\n\n .card-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .card-name[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 500;\n color: #111827;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .card-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #6B7280;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #9CA3AF;\n }\n\n .artifact-type[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: #F3F4F6;\n border-radius: 4px;\n font-weight: 500;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 150ms ease;\n }\n\n .collection-card[_ngcontent-%COMP%]:hover .card-actions[_ngcontent-%COMP%], \n .artifact-card[_ngcontent-%COMP%]:hover .card-actions[_ngcontent-%COMP%] {\n opacity: 1;\n }\n\n .card-action-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n color: #6B7280;\n cursor: pointer;\n transition: all 150ms ease;\n }\n\n .card-action-btn[_ngcontent-%COMP%]:hover {\n background: #F9FAFB;\n color: #111827;\n border-color: #D1D5DB;\n }\n\n .card-action-btn.danger[_ngcontent-%COMP%]:hover {\n background: #FEE2E2;\n color: #DC2626;\n border-color: #FCA5A5;\n }"] });
782
+ i0.ɵɵproperty("isOpen", ctx.isShareModalOpen)("collection", ctx.sharingCollection)("currentUser", ctx.currentUser)("currentUserPermissions", ctx.sharingCollection ? ctx.userPermissions.get(ctx.sharingCollection.ID) || null : null);
783
+ } }, dependencies: [i5.NgForOf, i5.NgIf, i6.CollectionShareModalComponent, i7.CollectionFormModalComponent, i8.ArtifactCreateModalComponent], styles: [".collections-view[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n }\n\n .collections-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 16px 24px;\n border-bottom: 1px solid #E5E7EB;\n gap: 16px;\n }\n\n .collections-breadcrumb[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .breadcrumb-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .breadcrumb-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #6B7280;\n font-size: 14px;\n }\n\n .breadcrumb-link[_ngcontent-%COMP%] {\n color: #111827;\n font-weight: 500;\n cursor: pointer;\n text-decoration: none;\n white-space: nowrap;\n transition: color 150ms ease;\n }\n\n .breadcrumb-link[_ngcontent-%COMP%]:hover {\n color: #1e40af;\n }\n\n .breadcrumb-link.active[_ngcontent-%COMP%] {\n color: #6B7280;\n cursor: default;\n }\n\n .breadcrumb-path[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n overflow-x: auto;\n }\n\n .breadcrumb-separator[_ngcontent-%COMP%] {\n color: #D1D5DB;\n font-size: 10px;\n }\n\n .collections-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n }\n\n .btn-primary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #1e40af;\n border: none;\n border-radius: 6px;\n color: white;\n font-size: 14px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n\n .btn-primary[_ngcontent-%COMP%]:hover {\n background: #1e3a8a;\n }\n\n .btn-secondary[_ngcontent-%COMP%] {\n padding: 8px 12px;\n background: transparent;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n cursor: pointer;\n color: #6B7280;\n transition: all 150ms ease;\n }\n\n .btn-secondary[_ngcontent-%COMP%]:hover {\n background: #F9FAFB;\n color: #111827;\n }\n\n .btn-sm[_ngcontent-%COMP%] {\n padding: 6px 12px;\n font-size: 13px;\n }\n\n .collections-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .loading-state[_ngcontent-%COMP%], .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #9CA3AF;\n max-width: 400px;\n margin: 0 auto;\n text-align: center;\n padding: 48px 24px;\n }\n\n .loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n margin-bottom: 24px;\n opacity: 0.3;\n color: #D1D5DB;\n }\n\n .empty-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: #374151;\n font-size: 20px;\n font-weight: 600;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: #6B7280;\n line-height: 1.5;\n }\n\n .loading-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }\n\n .empty-state[_ngcontent-%COMP%] .btn-primary[_ngcontent-%COMP%] {\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n }\n\n .empty-state[_ngcontent-%COMP%] .btn-primary[_ngcontent-%COMP%]:hover {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n }\n\n .content-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 32px;\n }\n\n .section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #111827;\n }\n\n .section-count[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 8px;\n background: #EFF6FF;\n border-radius: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #1e40af;\n }\n\n .collection-grid[_ngcontent-%COMP%], .artifact-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 16px;\n }\n\n .collection-card[_ngcontent-%COMP%], .artifact-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: start;\n gap: 16px;\n padding: 20px;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 8px;\n cursor: pointer;\n transition: all 150ms ease;\n position: relative;\n }\n\n .collection-card[_ngcontent-%COMP%]:hover, .artifact-card[_ngcontent-%COMP%]:hover {\n border-color: #1e40af;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n }\n\n .card-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #EFF6FF;\n border-radius: 8px;\n flex-shrink: 0;\n position: relative;\n }\n\n .card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: #1e40af;\n }\n\n .shared-badge[_ngcontent-%COMP%] {\n position: absolute;\n top: -4px;\n right: -4px;\n width: 18px;\n height: 18px;\n background: #10B981;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid white;\n }\n\n .shared-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 9px;\n color: white;\n }\n\n .artifact-icon[_ngcontent-%COMP%] {\n background: #F0FDF4;\n }\n\n .artifact-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #059669;\n }\n\n .card-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .card-name[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 500;\n color: #111827;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .card-owner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 6px;\n font-size: 12px;\n color: #6B7280;\n }\n\n .card-owner[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n }\n\n .card-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #6B7280;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #9CA3AF;\n }\n\n .artifact-type[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: #F3F4F6;\n border-radius: 4px;\n font-weight: 500;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 150ms ease;\n }\n\n .collection-card[_ngcontent-%COMP%]:hover .card-actions[_ngcontent-%COMP%], \n .artifact-card[_ngcontent-%COMP%]:hover .card-actions[_ngcontent-%COMP%] {\n opacity: 1;\n }\n\n .card-action-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n color: #6B7280;\n cursor: pointer;\n transition: all 150ms ease;\n }\n\n .card-action-btn[_ngcontent-%COMP%]:hover {\n background: #F9FAFB;\n color: #111827;\n border-color: #D1D5DB;\n }\n\n .card-action-btn.danger[_ngcontent-%COMP%]:hover {\n background: #FEE2E2;\n color: #DC2626;\n border-color: #FCA5A5;\n }"] });
432
784
  }
433
785
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CollectionsFullViewComponent, [{
434
786
  type: Component,
@@ -451,16 +803,8 @@ export class CollectionsFullViewComponent {
451
803
  </ng-container>
452
804
  </span>
453
805
  </div>
454
- <div class="collections-search">
455
- <i class="fas fa-search"></i>
456
- <input type="text"
457
- [(ngModel)]="searchQuery"
458
- (ngModelChange)="onSearchChange($event)"
459
- placeholder="Search collections and artifacts..."
460
- class="collection-search-input">
461
- </div>
462
806
  <div class="collections-actions">
463
- <button class="btn-primary" (click)="createCollection()" title="New Collection">
807
+ <button class="btn-primary" (click)="createCollection()" *ngIf="canEditCurrent()" title="New Collection">
464
808
  <i class="fas fa-folder-plus"></i>
465
809
  New Collection
466
810
  </button>
@@ -476,46 +820,46 @@ export class CollectionsFullViewComponent {
476
820
  <p>Loading collections...</p>
477
821
  </div>
478
822
 
479
- <div *ngIf="!isLoading && collections.length === 0 && artifacts.length === 0 && !searchQuery" class="empty-state">
480
- <i class="fas fa-folder-open"></i>
481
- <h3>No collections yet</h3>
482
- <p>Create your first collection to organize artifacts</p>
483
- <button class="btn-primary" (click)="createCollection()">
484
- <i class="fas fa-folder-plus"></i>
485
- Create Collection
486
- </button>
487
- </div>
488
-
489
- <div *ngIf="!isLoading && collections.length === 0 && artifacts.length === 0 && searchQuery" class="empty-state">
490
- <i class="fas fa-search"></i>
491
- <p>No collections or artifacts found</p>
492
- </div>
493
-
494
- <div *ngIf="!isLoading && (filteredCollections.length > 0 || filteredArtifacts.length > 0)" class="content-grid">
823
+ <div *ngIf="!isLoading" class="content-grid">
495
824
  <!-- Sub-collections -->
496
- <div class="section" *ngIf="filteredCollections.length > 0">
825
+ <div class="section">
497
826
  <div class="section-header">
498
827
  <h3>Collections</h3>
499
828
  <span class="section-count">{{ filteredCollections.length }}</span>
829
+ <button class="btn-primary btn-sm" (click)="createCollection()" *ngIf="canEditCurrent()">
830
+ <i class="fas fa-plus"></i>
831
+ New Collection
832
+ </button>
500
833
  </div>
501
- <div class="collection-grid">
834
+ <div class="collection-grid" *ngIf="filteredCollections.length > 0">
502
835
  <div *ngFor="let collection of filteredCollections"
503
836
  class="collection-card"
504
837
  (click)="openCollection(collection)">
505
838
  <div class="card-icon">
506
839
  <i class="fas fa-folder"></i>
840
+ <!-- Shared indicator -->
841
+ <div class="shared-badge" *ngIf="isShared(collection)" title="Shared with you">
842
+ <i class="fas fa-users"></i>
843
+ </div>
507
844
  </div>
508
845
  <div class="card-content">
509
846
  <div class="card-name">{{ collection.Name }}</div>
847
+ <div class="card-owner" *ngIf="isShared(collection) && collection.Owner">
848
+ <i class="fas fa-user"></i>
849
+ <span>{{ collection.Owner }}</span>
850
+ </div>
510
851
  <div class="card-description" *ngIf="collection.Description">
511
852
  {{ collection.Description }}
512
853
  </div>
513
854
  </div>
514
855
  <div class="card-actions" (click)="$event.stopPropagation()">
515
- <button class="card-action-btn" (click)="editCollection(collection)" title="Edit">
856
+ <button class="card-action-btn" *ngIf="canShare(collection)" (click)="shareCollection(collection)" title="Share">
857
+ <i class="fas fa-share-alt"></i>
858
+ </button>
859
+ <button class="card-action-btn" *ngIf="canEdit(collection)" (click)="editCollection(collection)" title="Edit">
516
860
  <i class="fas fa-edit"></i>
517
861
  </button>
518
- <button class="card-action-btn danger" (click)="deleteCollection(collection)" title="Delete">
862
+ <button class="card-action-btn danger" *ngIf="canDelete(collection)" (click)="deleteCollection(collection)" title="Delete">
519
863
  <i class="fas fa-trash"></i>
520
864
  </button>
521
865
  </div>
@@ -524,16 +868,16 @@ export class CollectionsFullViewComponent {
524
868
  </div>
525
869
 
526
870
  <!-- Artifacts in current collection -->
527
- <div class="section" *ngIf="filteredArtifacts.length > 0">
871
+ <div class="section" *ngIf="currentCollectionId">
528
872
  <div class="section-header">
529
873
  <h3>Artifacts</h3>
530
874
  <span class="section-count">{{ filteredArtifacts.length }}</span>
531
- <button class="btn-secondary btn-sm" (click)="addArtifact()" *ngIf="currentCollectionId">
875
+ <button class="btn-primary btn-sm" (click)="addArtifact()" *ngIf="canEditCurrent()">
532
876
  <i class="fas fa-plus"></i>
533
877
  Add Artifact
534
878
  </button>
535
879
  </div>
536
- <div class="artifact-grid">
880
+ <div class="artifact-grid" *ngIf="filteredArtifacts.length > 0">
537
881
  <div *ngFor="let artifact of filteredArtifacts"
538
882
  class="artifact-card"
539
883
  (click)="viewArtifact(artifact)">
@@ -547,7 +891,7 @@ export class CollectionsFullViewComponent {
547
891
  </div>
548
892
  </div>
549
893
  <div class="card-actions" (click)="$event.stopPropagation()">
550
- <button class="card-action-btn" (click)="removeArtifact(artifact)" title="Remove from collection" *ngIf="currentCollectionId">
894
+ <button class="card-action-btn" (click)="removeArtifact(artifact)" title="Remove from collection" *ngIf="canDeleteCurrent()">
551
895
  <i class="fas fa-times"></i>
552
896
  </button>
553
897
  </div>
@@ -568,11 +912,33 @@ export class CollectionsFullViewComponent {
568
912
  (saved)="onCollectionSaved($event)"
569
913
  (cancelled)="onFormCancelled()">
570
914
  </mj-collection-form-modal>
571
- `, styles: ["\n .collections-view {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n }\n\n .collections-header {\n display: flex;\n align-items: center;\n padding: 16px 24px;\n border-bottom: 1px solid #E5E7EB;\n gap: 16px;\n }\n\n .collections-breadcrumb {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .breadcrumb-item {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .breadcrumb-item i {\n color: #6B7280;\n font-size: 14px;\n }\n\n .breadcrumb-link {\n color: #111827;\n font-weight: 500;\n cursor: pointer;\n text-decoration: none;\n white-space: nowrap;\n transition: color 150ms ease;\n }\n\n .breadcrumb-link:hover {\n color: #1e40af;\n }\n\n .breadcrumb-link.active {\n color: #6B7280;\n cursor: default;\n }\n\n .breadcrumb-path {\n display: flex;\n align-items: center;\n gap: 8px;\n overflow-x: auto;\n }\n\n .breadcrumb-separator {\n color: #D1D5DB;\n font-size: 10px;\n }\n\n .collections-search {\n display: flex;\n align-items: center;\n gap: 8px;\n background: #F9FAFB;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n padding: 8px 12px;\n min-width: 300px;\n }\n\n .collections-search i {\n color: #9CA3AF;\n font-size: 14px;\n }\n\n .collection-search-input {\n border: none;\n background: transparent;\n outline: none;\n font-size: 14px;\n flex: 1;\n color: #111827;\n }\n\n .collection-search-input::placeholder {\n color: #9CA3AF;\n }\n\n .collections-actions {\n display: flex;\n gap: 8px;\n }\n\n .btn-primary {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #1e40af;\n border: none;\n border-radius: 6px;\n color: white;\n font-size: 14px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n\n .btn-primary:hover {\n background: #1e3a8a;\n }\n\n .btn-secondary {\n padding: 8px 12px;\n background: transparent;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n cursor: pointer;\n color: #6B7280;\n transition: all 150ms ease;\n }\n\n .btn-secondary:hover {\n background: #F9FAFB;\n color: #111827;\n }\n\n .btn-sm {\n padding: 6px 12px;\n font-size: 13px;\n }\n\n .collections-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .loading-state, .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #9CA3AF;\n max-width: 400px;\n margin: 0 auto;\n text-align: center;\n padding: 48px 24px;\n }\n\n .loading-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state i {\n font-size: 64px;\n margin-bottom: 24px;\n opacity: 0.3;\n color: #D1D5DB;\n }\n\n .empty-state h3 {\n margin: 0 0 8px 0;\n color: #374151;\n font-size: 20px;\n font-weight: 600;\n }\n\n .empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: #6B7280;\n line-height: 1.5;\n }\n\n .loading-state p {\n margin: 0;\n font-size: 14px;\n }\n\n .empty-state .btn-primary {\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n }\n\n .empty-state .btn-primary:hover {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n }\n\n .content-grid {\n display: flex;\n flex-direction: column;\n gap: 32px;\n }\n\n .section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #111827;\n }\n\n .section-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 8px;\n background: #EFF6FF;\n border-radius: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #1e40af;\n }\n\n .collection-grid, .artifact-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 16px;\n }\n\n .collection-card, .artifact-card {\n display: flex;\n align-items: start;\n gap: 16px;\n padding: 20px;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 8px;\n cursor: pointer;\n transition: all 150ms ease;\n position: relative;\n }\n\n .collection-card:hover, .artifact-card:hover {\n border-color: #1e40af;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n }\n\n .card-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #EFF6FF;\n border-radius: 8px;\n flex-shrink: 0;\n }\n\n .card-icon i {\n font-size: 20px;\n color: #1e40af;\n }\n\n .artifact-icon {\n background: #F0FDF4;\n }\n\n .artifact-icon i {\n color: #059669;\n }\n\n .card-content {\n flex: 1;\n min-width: 0;\n }\n\n .card-name {\n font-size: 15px;\n font-weight: 500;\n color: #111827;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .card-description {\n font-size: 13px;\n color: #6B7280;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta {\n font-size: 12px;\n color: #9CA3AF;\n }\n\n .artifact-type {\n padding: 2px 8px;\n background: #F3F4F6;\n border-radius: 4px;\n font-weight: 500;\n }\n\n .card-actions {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 150ms ease;\n }\n\n .collection-card:hover .card-actions,\n .artifact-card:hover .card-actions {\n opacity: 1;\n }\n\n .card-action-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n color: #6B7280;\n cursor: pointer;\n transition: all 150ms ease;\n }\n\n .card-action-btn:hover {\n background: #F9FAFB;\n color: #111827;\n border-color: #D1D5DB;\n }\n\n .card-action-btn.danger:hover {\n background: #FEE2E2;\n color: #DC2626;\n border-color: #FCA5A5;\n }\n "] }]
572
- }], () => [{ type: i1.DialogService }, { type: i2.ArtifactStateService }], { environmentId: [{
915
+
916
+ <!-- Artifact Create Modal -->
917
+ <mj-artifact-create-modal
918
+ [isOpen]="isArtifactModalOpen"
919
+ [collectionId]="currentCollectionId || ''"
920
+ [environmentId]="environmentId"
921
+ [currentUser]="currentUser"
922
+ (saved)="onArtifactSaved($event)"
923
+ (cancelled)="onArtifactModalCancelled()">
924
+ </mj-artifact-create-modal>
925
+
926
+ <!-- Share Modal -->
927
+ <mj-collection-share-modal
928
+ [isOpen]="isShareModalOpen"
929
+ [collection]="sharingCollection"
930
+ [currentUser]="currentUser"
931
+ [currentUserPermissions]="sharingCollection ? userPermissions.get(sharingCollection.ID) || null : null"
932
+ (saved)="onPermissionsChanged()"
933
+ (cancelled)="onShareModalCancelled()">
934
+ </mj-collection-share-modal>
935
+ `, styles: ["\n .collections-view {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: white;\n }\n\n .collections-header {\n display: flex;\n align-items: center;\n padding: 16px 24px;\n border-bottom: 1px solid #E5E7EB;\n gap: 16px;\n }\n\n .collections-breadcrumb {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .breadcrumb-item {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .breadcrumb-item i {\n color: #6B7280;\n font-size: 14px;\n }\n\n .breadcrumb-link {\n color: #111827;\n font-weight: 500;\n cursor: pointer;\n text-decoration: none;\n white-space: nowrap;\n transition: color 150ms ease;\n }\n\n .breadcrumb-link:hover {\n color: #1e40af;\n }\n\n .breadcrumb-link.active {\n color: #6B7280;\n cursor: default;\n }\n\n .breadcrumb-path {\n display: flex;\n align-items: center;\n gap: 8px;\n overflow-x: auto;\n }\n\n .breadcrumb-separator {\n color: #D1D5DB;\n font-size: 10px;\n }\n\n .collections-actions {\n display: flex;\n gap: 8px;\n }\n\n .btn-primary {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #1e40af;\n border: none;\n border-radius: 6px;\n color: white;\n font-size: 14px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n\n .btn-primary:hover {\n background: #1e3a8a;\n }\n\n .btn-secondary {\n padding: 8px 12px;\n background: transparent;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n cursor: pointer;\n color: #6B7280;\n transition: all 150ms ease;\n }\n\n .btn-secondary:hover {\n background: #F9FAFB;\n color: #111827;\n }\n\n .btn-sm {\n padding: 6px 12px;\n font-size: 13px;\n }\n\n .collections-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .loading-state, .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #9CA3AF;\n max-width: 400px;\n margin: 0 auto;\n text-align: center;\n padding: 48px 24px;\n }\n\n .loading-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .empty-state i {\n font-size: 64px;\n margin-bottom: 24px;\n opacity: 0.3;\n color: #D1D5DB;\n }\n\n .empty-state h3 {\n margin: 0 0 8px 0;\n color: #374151;\n font-size: 20px;\n font-weight: 600;\n }\n\n .empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: #6B7280;\n line-height: 1.5;\n }\n\n .loading-state p {\n margin: 0;\n font-size: 14px;\n }\n\n .empty-state .btn-primary {\n padding: 10px 20px;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n }\n\n .empty-state .btn-primary:hover {\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n }\n\n .content-grid {\n display: flex;\n flex-direction: column;\n gap: 32px;\n }\n\n .section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #111827;\n }\n\n .section-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 8px;\n background: #EFF6FF;\n border-radius: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #1e40af;\n }\n\n .collection-grid, .artifact-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 16px;\n }\n\n .collection-card, .artifact-card {\n display: flex;\n align-items: start;\n gap: 16px;\n padding: 20px;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 8px;\n cursor: pointer;\n transition: all 150ms ease;\n position: relative;\n }\n\n .collection-card:hover, .artifact-card:hover {\n border-color: #1e40af;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n }\n\n .card-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #EFF6FF;\n border-radius: 8px;\n flex-shrink: 0;\n position: relative;\n }\n\n .card-icon i {\n font-size: 20px;\n color: #1e40af;\n }\n\n .shared-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n width: 18px;\n height: 18px;\n background: #10B981;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid white;\n }\n\n .shared-badge i {\n font-size: 9px;\n color: white;\n }\n\n .artifact-icon {\n background: #F0FDF4;\n }\n\n .artifact-icon i {\n color: #059669;\n }\n\n .card-content {\n flex: 1;\n min-width: 0;\n }\n\n .card-name {\n font-size: 15px;\n font-weight: 500;\n color: #111827;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .card-owner {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 6px;\n font-size: 12px;\n color: #6B7280;\n }\n\n .card-owner i {\n font-size: 10px;\n }\n\n .card-description {\n font-size: 13px;\n color: #6B7280;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta {\n font-size: 12px;\n color: #9CA3AF;\n }\n\n .artifact-type {\n padding: 2px 8px;\n background: #F3F4F6;\n border-radius: 4px;\n font-weight: 500;\n }\n\n .card-actions {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 150ms ease;\n }\n\n .collection-card:hover .card-actions,\n .artifact-card:hover .card-actions {\n opacity: 1;\n }\n\n .card-action-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border: 1px solid #E5E7EB;\n border-radius: 6px;\n color: #6B7280;\n cursor: pointer;\n transition: all 150ms ease;\n }\n\n .card-action-btn:hover {\n background: #F9FAFB;\n color: #111827;\n border-color: #D1D5DB;\n }\n\n .card-action-btn.danger:hover {\n background: #FEE2E2;\n color: #DC2626;\n border-color: #FCA5A5;\n }\n "] }]
936
+ }], () => [{ type: i1.DialogService }, { type: i2.ArtifactStateService }, { type: i3.CollectionStateService }, { type: i4.CollectionPermissionService }], { environmentId: [{
573
937
  type: Input
574
938
  }], currentUser: [{
575
939
  type: Input
940
+ }], collectionNavigated: [{
941
+ type: Output
576
942
  }] }); })();
577
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CollectionsFullViewComponent, { className: "CollectionsFullViewComponent", filePath: "src/lib/components/collection/collections-full-view.component.ts", lineNumber: 514 }); })();
943
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CollectionsFullViewComponent, { className: "CollectionsFullViewComponent", filePath: "src/lib/components/collection/collections-full-view.component.ts", lineNumber: 533 }); })();
578
944
  //# sourceMappingURL=collections-full-view.component.js.map