@memberjunction/ng-explorer-core 6.2.0-edge.0 → 6.2.0-edge.1

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 (178) hide show
  1. package/dist/app-routing.module.d.ts +23 -1
  2. package/dist/app-routing.module.d.ts.map +1 -1
  3. package/dist/app-routing.module.js +50 -14
  4. package/dist/app-routing.module.js.map +1 -1
  5. package/dist/lib/command-palette/command-palette.component.d.ts +7 -1
  6. package/dist/lib/command-palette/command-palette.component.d.ts.map +1 -1
  7. package/dist/lib/command-palette/command-palette.component.js +28 -14
  8. package/dist/lib/command-palette/command-palette.component.js.map +1 -1
  9. package/dist/lib/conversation-feedback/conversation-feedback.d.ts +103 -13
  10. package/dist/lib/conversation-feedback/conversation-feedback.d.ts.map +1 -1
  11. package/dist/lib/conversation-feedback/conversation-feedback.js +219 -67
  12. package/dist/lib/conversation-feedback/conversation-feedback.js.map +1 -1
  13. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts +64 -10
  14. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts.map +1 -1
  15. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js +172 -66
  16. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js.map +1 -1
  17. package/dist/lib/generic/resource-container-component.d.ts +19 -5
  18. package/dist/lib/generic/resource-container-component.d.ts.map +1 -1
  19. package/dist/lib/generic/resource-container-component.js +43 -13
  20. package/dist/lib/generic/resource-container-component.js.map +1 -1
  21. package/dist/lib/guards/entities.guard.d.ts +4 -0
  22. package/dist/lib/guards/entities.guard.d.ts.map +1 -1
  23. package/dist/lib/guards/entities.guard.js +10 -2
  24. package/dist/lib/guards/entities.guard.js.map +1 -1
  25. package/dist/lib/identity-claims/claim-redeem.component.d.ts +4 -0
  26. package/dist/lib/identity-claims/claim-redeem.component.d.ts.map +1 -1
  27. package/dist/lib/identity-claims/claim-redeem.component.js +10 -2
  28. package/dist/lib/identity-claims/claim-redeem.component.js.map +1 -1
  29. package/dist/lib/oauth/oauth-callback.component.d.ts +4 -0
  30. package/dist/lib/oauth/oauth-callback.component.d.ts.map +1 -1
  31. package/dist/lib/oauth/oauth-callback.component.js +10 -2
  32. package/dist/lib/oauth/oauth-callback.component.js.map +1 -1
  33. package/dist/lib/profile/profile-dialog.component.d.ts.map +1 -1
  34. package/dist/lib/profile/profile-dialog.component.js +194 -191
  35. package/dist/lib/profile/profile-dialog.component.js.map +1 -1
  36. package/dist/lib/resource-wrappers/artifact-resource.component.d.ts +20 -2
  37. package/dist/lib/resource-wrappers/artifact-resource.component.d.ts.map +1 -1
  38. package/dist/lib/resource-wrappers/artifact-resource.component.js +33 -9
  39. package/dist/lib/resource-wrappers/artifact-resource.component.js.map +1 -1
  40. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts +80 -10
  41. package/dist/lib/resource-wrappers/chat-collections-resource.component.d.ts.map +1 -1
  42. package/dist/lib/resource-wrappers/chat-collections-resource.component.js +168 -56
  43. package/dist/lib/resource-wrappers/chat-collections-resource.component.js.map +1 -1
  44. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +186 -23
  45. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
  46. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +500 -177
  47. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
  48. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts +2 -0
  49. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts.map +1 -1
  50. package/dist/lib/resource-wrappers/chat-deeplink-params.js +5 -1
  51. package/dist/lib/resource-wrappers/chat-deeplink-params.js.map +1 -1
  52. package/dist/lib/resource-wrappers/chat-search-routing.d.ts +25 -0
  53. package/dist/lib/resource-wrappers/chat-search-routing.d.ts.map +1 -0
  54. package/dist/lib/resource-wrappers/chat-search-routing.js +24 -0
  55. package/dist/lib/resource-wrappers/chat-search-routing.js.map +1 -0
  56. package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts +14 -2
  57. package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts.map +1 -1
  58. package/dist/lib/resource-wrappers/chat-tasks-resource.component.js +32 -8
  59. package/dist/lib/resource-wrappers/chat-tasks-resource.component.js.map +1 -1
  60. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts +54 -8
  61. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
  62. package/dist/lib/resource-wrappers/dashboard-resource.component.js +138 -46
  63. package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
  64. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts +223 -30
  65. package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts.map +1 -1
  66. package/dist/lib/resource-wrappers/livekit-room-resource.component.js +438 -118
  67. package/dist/lib/resource-wrappers/livekit-room-resource.component.js.map +1 -1
  68. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts +7 -3
  69. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts.map +1 -1
  70. package/dist/lib/resource-wrappers/search-results-resource.component.js +23 -15
  71. package/dist/lib/resource-wrappers/search-results-resource.component.js.map +1 -1
  72. package/dist/lib/resource-wrappers/view-resource.component.d.ts +68 -10
  73. package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
  74. package/dist/lib/resource-wrappers/view-resource.component.js +178 -62
  75. package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
  76. package/dist/lib/services/startup-validation.service.d.ts +4 -0
  77. package/dist/lib/services/startup-validation.service.d.ts.map +1 -1
  78. package/dist/lib/services/startup-validation.service.js +10 -2
  79. package/dist/lib/services/startup-validation.service.js.map +1 -1
  80. package/dist/lib/services/system-validation.service.d.ts +13 -1
  81. package/dist/lib/services/system-validation.service.d.ts.map +1 -1
  82. package/dist/lib/services/system-validation.service.js +29 -5
  83. package/dist/lib/services/system-validation.service.js.map +1 -1
  84. package/dist/lib/shell/components/dialogs/app-access-dialog.component.d.ts +54 -4
  85. package/dist/lib/shell/components/dialogs/app-access-dialog.component.d.ts.map +1 -1
  86. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js +130 -40
  87. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js.map +1 -1
  88. package/dist/lib/shell/components/header/app-nav.component.d.ts +29 -1
  89. package/dist/lib/shell/components/header/app-nav.component.d.ts.map +1 -1
  90. package/dist/lib/shell/components/header/app-nav.component.js +56 -12
  91. package/dist/lib/shell/components/header/app-nav.component.js.map +1 -1
  92. package/dist/lib/shell/components/header/app-switcher.component.d.ts +33 -5
  93. package/dist/lib/shell/components/header/app-switcher.component.d.ts.map +1 -1
  94. package/dist/lib/shell/components/header/app-switcher.component.js +85 -29
  95. package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
  96. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js +1 -1
  97. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js.map +1 -1
  98. package/dist/lib/shell/components/tabs/component-cache-manager.d.ts +50 -6
  99. package/dist/lib/shell/components/tabs/component-cache-manager.d.ts.map +1 -1
  100. package/dist/lib/shell/components/tabs/component-cache-manager.js +95 -22
  101. package/dist/lib/shell/components/tabs/component-cache-manager.js.map +1 -1
  102. package/dist/lib/shell/components/tabs/tab-container.component.d.ts +91 -9
  103. package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
  104. package/dist/lib/shell/components/tabs/tab-container.component.js +257 -101
  105. package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
  106. package/dist/lib/shell/loading-themes.d.ts +16 -12
  107. package/dist/lib/shell/loading-themes.d.ts.map +1 -1
  108. package/dist/lib/shell/loading-themes.js +145 -137
  109. package/dist/lib/shell/loading-themes.js.map +1 -1
  110. package/dist/lib/shell/readiness-beacon.d.ts +2 -0
  111. package/dist/lib/shell/readiness-beacon.d.ts.map +1 -1
  112. package/dist/lib/shell/readiness-beacon.js +5 -1
  113. package/dist/lib/shell/readiness-beacon.js.map +1 -1
  114. package/dist/lib/shell/services/about-dialog.service.d.ts +6 -0
  115. package/dist/lib/shell/services/about-dialog.service.d.ts.map +1 -1
  116. package/dist/lib/shell/services/about-dialog.service.js +16 -4
  117. package/dist/lib/shell/services/about-dialog.service.js.map +1 -1
  118. package/dist/lib/shell/services/profile-dialog.service.d.ts +6 -0
  119. package/dist/lib/shell/services/profile-dialog.service.d.ts.map +1 -1
  120. package/dist/lib/shell/services/profile-dialog.service.js +16 -4
  121. package/dist/lib/shell/services/profile-dialog.service.js.map +1 -1
  122. package/dist/lib/shell/services/user-sharing-center-dialog.service.d.ts +6 -0
  123. package/dist/lib/shell/services/user-sharing-center-dialog.service.d.ts.map +1 -1
  124. package/dist/lib/shell/services/user-sharing-center-dialog.service.js +15 -3
  125. package/dist/lib/shell/services/user-sharing-center-dialog.service.js.map +1 -1
  126. package/dist/lib/shell/shell.component.d.ts +262 -39
  127. package/dist/lib/shell/shell.component.d.ts.map +1 -1
  128. package/dist/lib/shell/shell.component.js +688 -292
  129. package/dist/lib/shell/shell.component.js.map +1 -1
  130. package/dist/lib/single-dashboard/Components/add-item/add-item.component.d.ts +64 -8
  131. package/dist/lib/single-dashboard/Components/add-item/add-item.component.d.ts.map +1 -1
  132. package/dist/lib/single-dashboard/Components/add-item/add-item.component.js +129 -37
  133. package/dist/lib/single-dashboard/Components/add-item/add-item.component.js.map +1 -1
  134. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.d.ts +26 -2
  135. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.d.ts.map +1 -1
  136. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js +48 -10
  137. package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js.map +1 -1
  138. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.d.ts +84 -6
  139. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.d.ts.map +1 -1
  140. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js +133 -29
  141. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js.map +1 -1
  142. package/dist/lib/single-dashboard/single-dashboard.component.d.ts +173 -23
  143. package/dist/lib/single-dashboard/single-dashboard.component.d.ts.map +1 -1
  144. package/dist/lib/single-dashboard/single-dashboard.component.js +383 -109
  145. package/dist/lib/single-dashboard/single-dashboard.component.js.map +1 -1
  146. package/dist/lib/single-list-detail/single-list-detail.component.d.ts +383 -55
  147. package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
  148. package/dist/lib/single-list-detail/single-list-detail.component.js +962 -326
  149. package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
  150. package/dist/lib/single-query/single-query.component.d.ts +22 -2
  151. package/dist/lib/single-query/single-query.component.d.ts.map +1 -1
  152. package/dist/lib/single-query/single-query.component.js +40 -10
  153. package/dist/lib/single-query/single-query.component.js.map +1 -1
  154. package/dist/lib/single-record/single-record.component.d.ts +41 -3
  155. package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
  156. package/dist/lib/single-record/single-record.component.js +71 -11
  157. package/dist/lib/single-record/single-record.component.js.map +1 -1
  158. package/dist/lib/single-search-result/single-search-result.component.d.ts +23 -3
  159. package/dist/lib/single-search-result/single-search-result.component.d.ts.map +1 -1
  160. package/dist/lib/single-search-result/single-search-result.component.js +42 -12
  161. package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
  162. package/dist/lib/system-validation/system-validation-banner.component.d.ts +12 -2
  163. package/dist/lib/system-validation/system-validation-banner.component.d.ts.map +1 -1
  164. package/dist/lib/system-validation/system-validation-banner.component.js +25 -5
  165. package/dist/lib/system-validation/system-validation-banner.component.js.map +1 -1
  166. package/dist/lib/user-menu/user-menu.types.d.ts +4 -0
  167. package/dist/lib/user-menu/user-menu.types.d.ts.map +1 -1
  168. package/dist/lib/user-menu/user-menu.types.js +10 -2
  169. package/dist/lib/user-menu/user-menu.types.js.map +1 -1
  170. package/dist/lib/user-notifications/user-notifications.component.d.ts +59 -7
  171. package/dist/lib/user-notifications/user-notifications.component.d.ts.map +1 -1
  172. package/dist/lib/user-notifications/user-notifications.component.js +151 -47
  173. package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
  174. package/dist/lib/user-profile/user-profile.component.d.ts +3 -2
  175. package/dist/lib/user-profile/user-profile.component.d.ts.map +1 -1
  176. package/dist/lib/user-profile/user-profile.component.js +5 -4
  177. package/dist/lib/user-profile/user-profile.component.js.map +1 -1
  178. package/package.json +52 -52
@@ -836,64 +836,320 @@ export class SingleListDetailComponent extends BaseAngularComponent {
836
836
  * those numbers drift from reality until a full page reload because the
837
837
  * stats component otherwise only loads on init.
838
838
  */
839
- statsRefreshTrigger = 0;
840
- bumpStatsRefresh() { this.statsRefreshTrigger++; }
841
- listDetailGrid;
839
+ StatsRefreshTrigger = 0;
840
+ /** @deprecated Use {@link StatsRefreshTrigger}. */
841
+ get statsRefreshTrigger() {
842
+ return this.StatsRefreshTrigger;
843
+ }
844
+ /** @deprecated Use {@link StatsRefreshTrigger}. */
845
+ set statsRefreshTrigger(value) {
846
+ this.StatsRefreshTrigger = value;
847
+ }
848
+ bumpStatsRefresh() { this.StatsRefreshTrigger++; }
849
+ ListDetailGrid;
850
+ /** @deprecated Use {@link ListDetailGrid}. */
851
+ get listDetailGrid() {
852
+ return this.ListDetailGrid;
853
+ }
854
+ /** @deprecated Use {@link ListDetailGrid}. */
855
+ set listDetailGrid(value) {
856
+ this.ListDetailGrid = value;
857
+ }
842
858
  // List record
843
- listRecord = null;
844
- showLoader = false;
859
+ ListRecord = null;
860
+ /** @deprecated Use {@link ListRecord}. */
861
+ get listRecord() {
862
+ return this.ListRecord;
863
+ }
864
+ /** @deprecated Use {@link ListRecord}. */
865
+ set listRecord(value) {
866
+ this.ListRecord = value;
867
+ }
868
+ ShowLoader = false;
869
+ /** @deprecated Use {@link ShowLoader}. */
870
+ get showLoader() {
871
+ return this.ShowLoader;
872
+ }
873
+ /** @deprecated Use {@link ShowLoader}. */
874
+ set showLoader(value) {
875
+ this.ShowLoader = value;
876
+ }
845
877
  // Permission-level gating (Phase 2.8). Resolved lazily after the list
846
878
  // loads. Capabilities is exposed to the template for `@if` gating; the
847
879
  // server-side enforcement remains the source of truth — these flags
848
880
  // are a UX convenience so users don't see buttons they'll be rejected on.
849
- capabilities = CapabilitiesForLevel(null);
850
- currentLevel = null;
881
+ Capabilities = CapabilitiesForLevel(null);
882
+ /** @deprecated Use {@link Capabilities}. */
883
+ get capabilities() {
884
+ return this.Capabilities;
885
+ }
886
+ /** @deprecated Use {@link Capabilities}. */
887
+ set capabilities(value) {
888
+ this.Capabilities = value;
889
+ }
890
+ CurrentLevel = null;
891
+ /** @deprecated Use {@link CurrentLevel}. */
892
+ get currentLevel() {
893
+ return this.CurrentLevel;
894
+ }
895
+ /** @deprecated Use {@link CurrentLevel}. */
896
+ set currentLevel(value) {
897
+ this.CurrentLevel = value;
898
+ }
851
899
  // Lineage / refresh-from-source state. `sourceViewName` is loaded after
852
900
  // listRecord so the lineage badge can show a friendly view name; the
853
901
  // refresh-mode default falls back to the list's RefreshMode field but
854
902
  // can be overridden per-user via localStorage (see `loadLastUsedMode`).
855
- sourceViewName = null;
903
+ SourceViewName = null;
904
+ /** @deprecated Use {@link SourceViewName}. */
905
+ get sourceViewName() {
906
+ return this.SourceViewName;
907
+ }
908
+ /** @deprecated Use {@link SourceViewName}. */
909
+ set sourceViewName(value) {
910
+ this.SourceViewName = value;
911
+ }
856
912
  // Bulk-edit (Phase 5.2). Status, Move, Copy, Remove. Move flows through
857
913
  // the delta-confirm dialog because it produces drops on the source list.
858
- bulkStatus = '';
859
- isApplyingBulkStatus = false;
914
+ BulkStatus = '';
915
+ /** @deprecated Use {@link BulkStatus}. */
916
+ get bulkStatus() {
917
+ return this.BulkStatus;
918
+ }
919
+ /** @deprecated Use {@link BulkStatus}. */
920
+ set bulkStatus(value) {
921
+ this.BulkStatus = value;
922
+ }
923
+ IsApplyingBulkStatus = false;
924
+ /** @deprecated Use {@link IsApplyingBulkStatus}. */
925
+ get isApplyingBulkStatus() {
926
+ return this.IsApplyingBulkStatus;
927
+ }
928
+ /** @deprecated Use {@link IsApplyingBulkStatus}. */
929
+ set isApplyingBulkStatus(value) {
930
+ this.IsApplyingBulkStatus = value;
931
+ }
860
932
  // Move / Copy state (mockups 23, 24). The picker dialog lists candidate
861
933
  // target lists scoped to the same entity. We load it lazily on first
862
934
  // open and cache for the session — opening the picker twice in a row
863
935
  // doesn't re-fetch unless the user explicitly hits "Refresh".
864
- showMoveCopyDialog = false;
865
- moveCopyMode = 'move';
866
- moveCopyTargetSearch = '';
867
- moveCopyTargetCandidates = [];
868
- moveCopyTargetCandidatesLoading = false;
869
- moveCopySelectedTarget = null;
870
- isApplyingMoveCopy = false;
871
- moveCopyProgress = 0;
872
- moveCopyTotal = 0;
936
+ ShowMoveCopyDialog = false;
937
+ /** @deprecated Use {@link ShowMoveCopyDialog}. */
938
+ get showMoveCopyDialog() {
939
+ return this.ShowMoveCopyDialog;
940
+ }
941
+ /** @deprecated Use {@link ShowMoveCopyDialog}. */
942
+ set showMoveCopyDialog(value) {
943
+ this.ShowMoveCopyDialog = value;
944
+ }
945
+ MoveCopyMode = 'move';
946
+ /** @deprecated Use {@link MoveCopyMode}. */
947
+ get moveCopyMode() {
948
+ return this.MoveCopyMode;
949
+ }
950
+ /** @deprecated Use {@link MoveCopyMode}. */
951
+ set moveCopyMode(value) {
952
+ this.MoveCopyMode = value;
953
+ }
954
+ MoveCopyTargetSearch = '';
955
+ /** @deprecated Use {@link MoveCopyTargetSearch}. */
956
+ get moveCopyTargetSearch() {
957
+ return this.MoveCopyTargetSearch;
958
+ }
959
+ /** @deprecated Use {@link MoveCopyTargetSearch}. */
960
+ set moveCopyTargetSearch(value) {
961
+ this.MoveCopyTargetSearch = value;
962
+ }
963
+ MoveCopyTargetCandidates = [];
964
+ /** @deprecated Use {@link MoveCopyTargetCandidates}. */
965
+ get moveCopyTargetCandidates() {
966
+ return this.MoveCopyTargetCandidates;
967
+ }
968
+ /** @deprecated Use {@link MoveCopyTargetCandidates}. */
969
+ set moveCopyTargetCandidates(value) {
970
+ this.MoveCopyTargetCandidates = value;
971
+ }
972
+ MoveCopyTargetCandidatesLoading = false;
973
+ /** @deprecated Use {@link MoveCopyTargetCandidatesLoading}. */
974
+ get moveCopyTargetCandidatesLoading() {
975
+ return this.MoveCopyTargetCandidatesLoading;
976
+ }
977
+ /** @deprecated Use {@link MoveCopyTargetCandidatesLoading}. */
978
+ set moveCopyTargetCandidatesLoading(value) {
979
+ this.MoveCopyTargetCandidatesLoading = value;
980
+ }
981
+ MoveCopySelectedTarget = null;
982
+ /** @deprecated Use {@link MoveCopySelectedTarget}. */
983
+ get moveCopySelectedTarget() {
984
+ return this.MoveCopySelectedTarget;
985
+ }
986
+ /** @deprecated Use {@link MoveCopySelectedTarget}. */
987
+ set moveCopySelectedTarget(value) {
988
+ this.MoveCopySelectedTarget = value;
989
+ }
990
+ IsApplyingMoveCopy = false;
991
+ /** @deprecated Use {@link IsApplyingMoveCopy}. */
992
+ get isApplyingMoveCopy() {
993
+ return this.IsApplyingMoveCopy;
994
+ }
995
+ /** @deprecated Use {@link IsApplyingMoveCopy}. */
996
+ set isApplyingMoveCopy(value) {
997
+ this.IsApplyingMoveCopy = value;
998
+ }
999
+ MoveCopyProgress = 0;
1000
+ /** @deprecated Use {@link MoveCopyProgress}. */
1001
+ get moveCopyProgress() {
1002
+ return this.MoveCopyProgress;
1003
+ }
1004
+ /** @deprecated Use {@link MoveCopyProgress}. */
1005
+ set moveCopyProgress(value) {
1006
+ this.MoveCopyProgress = value;
1007
+ }
1008
+ MoveCopyTotal = 0;
1009
+ /** @deprecated Use {@link MoveCopyTotal}. */
1010
+ get moveCopyTotal() {
1011
+ return this.MoveCopyTotal;
1012
+ }
1013
+ /** @deprecated Use {@link MoveCopyTotal}. */
1014
+ set moveCopyTotal(value) {
1015
+ this.MoveCopyTotal = value;
1016
+ }
873
1017
  // Move delta-confirm. Built locally from the in-hand RecordIDs — no
874
1018
  // server round-trip needed since we already know exactly what will be
875
1019
  // added to the target and removed from the source. The drop-guard is
876
1020
  // enforced at confirm time (mode='move' + ack checkbox).
877
- moveDeltaConfirmVisible = false;
878
- moveDelta = null;
1021
+ MoveDeltaConfirmVisible = false;
1022
+ /** @deprecated Use {@link MoveDeltaConfirmVisible}. */
1023
+ get moveDeltaConfirmVisible() {
1024
+ return this.MoveDeltaConfirmVisible;
1025
+ }
1026
+ /** @deprecated Use {@link MoveDeltaConfirmVisible}. */
1027
+ set moveDeltaConfirmVisible(value) {
1028
+ this.MoveDeltaConfirmVisible = value;
1029
+ }
1030
+ MoveDelta = null;
1031
+ /** @deprecated Use {@link MoveDelta}. */
1032
+ get moveDelta() {
1033
+ return this.MoveDelta;
1034
+ }
1035
+ /** @deprecated Use {@link MoveDelta}. */
1036
+ set moveDelta(value) {
1037
+ this.MoveDelta = value;
1038
+ }
879
1039
  // Export picker (Phase 5.1, mockup 26). Opens before any export; lets
880
1040
  // the user pick format + which entity fields to include. Fields are
881
1041
  // resolved from EntityInfo on the loaded provider — no separate fetch.
882
- showExportDialog = false;
883
- exportFormat = 'excel';
884
- exportFields = [];
885
- exportRecordCount = 0;
886
- isExporting = false;
887
- refreshMode = 'Additive';
888
- isPreviewingRefresh = false;
889
- isApplyingRefresh = false;
890
- refreshDelta = null;
891
- refreshConfirmVisible = false;
1042
+ ShowExportDialog = false;
1043
+ /** @deprecated Use {@link ShowExportDialog}. */
1044
+ get showExportDialog() {
1045
+ return this.ShowExportDialog;
1046
+ }
1047
+ /** @deprecated Use {@link ShowExportDialog}. */
1048
+ set showExportDialog(value) {
1049
+ this.ShowExportDialog = value;
1050
+ }
1051
+ ExportFormat = 'excel';
1052
+ /** @deprecated Use {@link ExportFormat}. */
1053
+ get exportFormat() {
1054
+ return this.ExportFormat;
1055
+ }
1056
+ /** @deprecated Use {@link ExportFormat}. */
1057
+ set exportFormat(value) {
1058
+ this.ExportFormat = value;
1059
+ }
1060
+ ExportFields = [];
1061
+ /** @deprecated Use {@link ExportFields}. */
1062
+ get exportFields() {
1063
+ return this.ExportFields;
1064
+ }
1065
+ /** @deprecated Use {@link ExportFields}. */
1066
+ set exportFields(value) {
1067
+ this.ExportFields = value;
1068
+ }
1069
+ ExportRecordCount = 0;
1070
+ /** @deprecated Use {@link ExportRecordCount}. */
1071
+ get exportRecordCount() {
1072
+ return this.ExportRecordCount;
1073
+ }
1074
+ /** @deprecated Use {@link ExportRecordCount}. */
1075
+ set exportRecordCount(value) {
1076
+ this.ExportRecordCount = value;
1077
+ }
1078
+ IsExporting = false;
1079
+ /** @deprecated Use {@link IsExporting}. */
1080
+ get isExporting() {
1081
+ return this.IsExporting;
1082
+ }
1083
+ /** @deprecated Use {@link IsExporting}. */
1084
+ set isExporting(value) {
1085
+ this.IsExporting = value;
1086
+ }
1087
+ RefreshMode = 'Additive';
1088
+ /** @deprecated Use {@link RefreshMode}. */
1089
+ get refreshMode() {
1090
+ return this.RefreshMode;
1091
+ }
1092
+ /** @deprecated Use {@link RefreshMode}. */
1093
+ set refreshMode(value) {
1094
+ this.RefreshMode = value;
1095
+ }
1096
+ IsPreviewingRefresh = false;
1097
+ /** @deprecated Use {@link IsPreviewingRefresh}. */
1098
+ get isPreviewingRefresh() {
1099
+ return this.IsPreviewingRefresh;
1100
+ }
1101
+ /** @deprecated Use {@link IsPreviewingRefresh}. */
1102
+ set isPreviewingRefresh(value) {
1103
+ this.IsPreviewingRefresh = value;
1104
+ }
1105
+ IsApplyingRefresh = false;
1106
+ /** @deprecated Use {@link IsApplyingRefresh}. */
1107
+ get isApplyingRefresh() {
1108
+ return this.IsApplyingRefresh;
1109
+ }
1110
+ /** @deprecated Use {@link IsApplyingRefresh}. */
1111
+ set isApplyingRefresh(value) {
1112
+ this.IsApplyingRefresh = value;
1113
+ }
1114
+ RefreshDelta = null;
1115
+ /** @deprecated Use {@link RefreshDelta}. */
1116
+ get refreshDelta() {
1117
+ return this.RefreshDelta;
1118
+ }
1119
+ /** @deprecated Use {@link RefreshDelta}. */
1120
+ set refreshDelta(value) {
1121
+ this.RefreshDelta = value;
1122
+ }
1123
+ RefreshConfirmVisible = false;
1124
+ /** @deprecated Use {@link RefreshConfirmVisible}. */
1125
+ get refreshConfirmVisible() {
1126
+ return this.RefreshConfirmVisible;
1127
+ }
1128
+ /** @deprecated Use {@link RefreshConfirmVisible}. */
1129
+ set refreshConfirmVisible(value) {
1130
+ this.RefreshConfirmVisible = value;
1131
+ }
892
1132
  // Grid state
893
- selectedKeys = [];
894
- rowCount = 0;
1133
+ SelectedKeys = [];
1134
+ /** @deprecated Use {@link SelectedKeys}. */
1135
+ get selectedKeys() {
1136
+ return this.SelectedKeys;
1137
+ }
1138
+ /** @deprecated Use {@link SelectedKeys}. */
1139
+ set selectedKeys(value) {
1140
+ this.SelectedKeys = value;
1141
+ }
1142
+ RowCount = 0;
1143
+ /** @deprecated Use {@link RowCount}. */
1144
+ get rowCount() {
1145
+ return this.RowCount;
1146
+ }
1147
+ /** @deprecated Use {@link RowCount}. */
1148
+ set rowCount(value) {
1149
+ this.RowCount = value;
1150
+ }
895
1151
  // Toolbar config - hide EDG toolbar, we'll use our own
896
- gridToolbarConfig = {
1152
+ GridToolbarConfig = {
897
1153
  showSearch: false,
898
1154
  showRefresh: false,
899
1155
  showAdd: false,
@@ -902,39 +1158,191 @@ export class SingleListDetailComponent extends BaseAngularComponent {
902
1158
  showRowCount: false,
903
1159
  showSelectionCount: false
904
1160
  };
1161
+ /** @deprecated Use {@link GridToolbarConfig}. */
1162
+ get gridToolbarConfig() {
1163
+ return this.GridToolbarConfig;
1164
+ }
1165
+ /** @deprecated Use {@link GridToolbarConfig}. */
1166
+ set gridToolbarConfig(value) {
1167
+ this.GridToolbarConfig = value;
1168
+ }
905
1169
  // Remove from list dialog
906
- showRemoveDialog = false;
907
- isRemoving = false;
908
- removeProgress = 0;
909
- removeTotal = 0;
1170
+ ShowRemoveDialog = false;
1171
+ /** @deprecated Use {@link ShowRemoveDialog}. */
1172
+ get showRemoveDialog() {
1173
+ return this.ShowRemoveDialog;
1174
+ }
1175
+ /** @deprecated Use {@link ShowRemoveDialog}. */
1176
+ set showRemoveDialog(value) {
1177
+ this.ShowRemoveDialog = value;
1178
+ }
1179
+ IsRemoving = false;
1180
+ /** @deprecated Use {@link IsRemoving}. */
1181
+ get isRemoving() {
1182
+ return this.IsRemoving;
1183
+ }
1184
+ /** @deprecated Use {@link IsRemoving}. */
1185
+ set isRemoving(value) {
1186
+ this.IsRemoving = value;
1187
+ }
1188
+ RemoveProgress = 0;
1189
+ /** @deprecated Use {@link RemoveProgress}. */
1190
+ get removeProgress() {
1191
+ return this.RemoveProgress;
1192
+ }
1193
+ /** @deprecated Use {@link RemoveProgress}. */
1194
+ set removeProgress(value) {
1195
+ this.RemoveProgress = value;
1196
+ }
1197
+ RemoveTotal = 0;
1198
+ /** @deprecated Use {@link RemoveTotal}. */
1199
+ get removeTotal() {
1200
+ return this.RemoveTotal;
1201
+ }
1202
+ /** @deprecated Use {@link RemoveTotal}. */
1203
+ set removeTotal(value) {
1204
+ this.RemoveTotal = value;
1205
+ }
910
1206
  // Add records dialog
911
- showAddRecordsDialog = false;
912
- addDialogLoading = false;
913
- addDialogSaving = false;
914
- addableRecords = [];
915
- addRecordsSearchFilter = "";
1207
+ ShowAddRecordsDialog = false;
1208
+ /** @deprecated Use {@link ShowAddRecordsDialog}. */
1209
+ get showAddRecordsDialog() {
1210
+ return this.ShowAddRecordsDialog;
1211
+ }
1212
+ /** @deprecated Use {@link ShowAddRecordsDialog}. */
1213
+ set showAddRecordsDialog(value) {
1214
+ this.ShowAddRecordsDialog = value;
1215
+ }
1216
+ AddDialogLoading = false;
1217
+ /** @deprecated Use {@link AddDialogLoading}. */
1218
+ get addDialogLoading() {
1219
+ return this.AddDialogLoading;
1220
+ }
1221
+ /** @deprecated Use {@link AddDialogLoading}. */
1222
+ set addDialogLoading(value) {
1223
+ this.AddDialogLoading = value;
1224
+ }
1225
+ AddDialogSaving = false;
1226
+ /** @deprecated Use {@link AddDialogSaving}. */
1227
+ get addDialogSaving() {
1228
+ return this.AddDialogSaving;
1229
+ }
1230
+ /** @deprecated Use {@link AddDialogSaving}. */
1231
+ set addDialogSaving(value) {
1232
+ this.AddDialogSaving = value;
1233
+ }
1234
+ AddableRecords = [];
1235
+ /** @deprecated Use {@link AddableRecords}. */
1236
+ get addableRecords() {
1237
+ return this.AddableRecords;
1238
+ }
1239
+ /** @deprecated Use {@link AddableRecords}. */
1240
+ set addableRecords(value) {
1241
+ this.AddableRecords = value;
1242
+ }
1243
+ AddRecordsSearchFilter = "";
1244
+ /** @deprecated Use {@link AddRecordsSearchFilter}. */
1245
+ get addRecordsSearchFilter() {
1246
+ return this.AddRecordsSearchFilter;
1247
+ }
1248
+ /** @deprecated Use {@link AddRecordsSearchFilter}. */
1249
+ set addRecordsSearchFilter(value) {
1250
+ this.AddRecordsSearchFilter = value;
1251
+ }
916
1252
  /** Empty-state title shown when an add-records search returns no matches. */
917
1253
  get AddRecordsNoMatchTitle() {
918
- return `No records found matching "${this.addRecordsSearchFilter}"`;
1254
+ return `No records found matching "${this.AddRecordsSearchFilter}"`;
1255
+ }
1256
+ ExistingListDetailIds = new Set();
1257
+ /** @deprecated Use {@link ExistingListDetailIds}. */
1258
+ get existingListDetailIds() {
1259
+ return this.ExistingListDetailIds;
1260
+ }
1261
+ /** @deprecated Use {@link ExistingListDetailIds}. */
1262
+ set existingListDetailIds(value) {
1263
+ this.ExistingListDetailIds = value;
1264
+ }
1265
+ AddProgress = 0;
1266
+ /** @deprecated Use {@link AddProgress}. */
1267
+ get addProgress() {
1268
+ return this.AddProgress;
1269
+ }
1270
+ /** @deprecated Use {@link AddProgress}. */
1271
+ set addProgress(value) {
1272
+ this.AddProgress = value;
1273
+ }
1274
+ AddTotal = 0;
1275
+ /** @deprecated Use {@link AddTotal}. */
1276
+ get addTotal() {
1277
+ return this.AddTotal;
1278
+ }
1279
+ /** @deprecated Use {@link AddTotal}. */
1280
+ set addTotal(value) {
1281
+ this.AddTotal = value;
919
1282
  }
920
- existingListDetailIds = new Set();
921
- addProgress = 0;
922
- addTotal = 0;
923
1283
  searchSubject = new Subject();
924
1284
  destroy$ = new Subject();
925
1285
  // Add from view dialog (existing)
926
- showAddFromViewDialog = false;
927
- showAddFromViewLoader = false;
928
- userViews = null;
929
- userViewsToAdd = [];
1286
+ ShowAddFromViewDialog = false;
1287
+ /** @deprecated Use {@link ShowAddFromViewDialog}. */
1288
+ get showAddFromViewDialog() {
1289
+ return this.ShowAddFromViewDialog;
1290
+ }
1291
+ /** @deprecated Use {@link ShowAddFromViewDialog}. */
1292
+ set showAddFromViewDialog(value) {
1293
+ this.ShowAddFromViewDialog = value;
1294
+ }
1295
+ ShowAddFromViewLoader = false;
1296
+ /** @deprecated Use {@link ShowAddFromViewLoader}. */
1297
+ get showAddFromViewLoader() {
1298
+ return this.ShowAddFromViewLoader;
1299
+ }
1300
+ /** @deprecated Use {@link ShowAddFromViewLoader}. */
1301
+ set showAddFromViewLoader(value) {
1302
+ this.ShowAddFromViewLoader = value;
1303
+ }
1304
+ UserViews = null;
1305
+ /** @deprecated Use {@link UserViews}. */
1306
+ get userViews() {
1307
+ return this.UserViews;
1308
+ }
1309
+ /** @deprecated Use {@link UserViews}. */
1310
+ set userViews(value) {
1311
+ this.UserViews = value;
1312
+ }
1313
+ UserViewsToAdd = [];
1314
+ /** @deprecated Use {@link UserViewsToAdd}. */
1315
+ get userViewsToAdd() {
1316
+ return this.UserViewsToAdd;
1317
+ }
1318
+ /** @deprecated Use {@link UserViewsToAdd}. */
1319
+ set userViewsToAdd(value) {
1320
+ this.UserViewsToAdd = value;
1321
+ }
930
1322
  /**
931
1323
  * Normalized-UUID set of the IDs in {@link userViewsToAdd}, kept in sync with that
932
1324
  * array. Lets {@link isViewSelected} (bound per-row in the dialog's @for, ~2x/row)
933
1325
  * do an O(1) lookup instead of scanning the array with UUIDsEqual on every check.
934
1326
  */
935
1327
  userViewsToAddIds = new Set();
936
- addFromViewProgress = 0;
937
- addFromViewTotal = 0;
1328
+ AddFromViewProgress = 0;
1329
+ /** @deprecated Use {@link AddFromViewProgress}. */
1330
+ get addFromViewProgress() {
1331
+ return this.AddFromViewProgress;
1332
+ }
1333
+ /** @deprecated Use {@link AddFromViewProgress}. */
1334
+ set addFromViewProgress(value) {
1335
+ this.AddFromViewProgress = value;
1336
+ }
1337
+ AddFromViewTotal = 0;
1338
+ /** @deprecated Use {@link AddFromViewTotal}. */
1339
+ get addFromViewTotal() {
1340
+ return this.AddFromViewTotal;
1341
+ }
1342
+ /** @deprecated Use {@link AddFromViewTotal}. */
1343
+ set addFromViewTotal(value) {
1344
+ this.AddFromViewTotal = value;
1345
+ }
938
1346
  /**
939
1347
  * Stored percent (0–100) for the progress bar. Backed instead of computed
940
1348
  * via getter because the original getter recomputed on every Angular CD
@@ -943,30 +1351,62 @@ export class SingleListDetailComponent extends BaseAngularComponent {
943
1351
  * ExpressionChangedAfterItHasBeenCheckedError floods in the console.
944
1352
  * Updated via setAddFromViewProgress(...) at controlled points.
945
1353
  */
946
- addFromViewProgressPercent = 0;
947
- fetchingRecordsToSave = false;
1354
+ AddFromViewProgressPercent = 0;
1355
+ /** @deprecated Use {@link AddFromViewProgressPercent}. */
1356
+ get addFromViewProgressPercent() {
1357
+ return this.AddFromViewProgressPercent;
1358
+ }
1359
+ /** @deprecated Use {@link AddFromViewProgressPercent}. */
1360
+ set addFromViewProgressPercent(value) {
1361
+ this.AddFromViewProgressPercent = value;
1362
+ }
1363
+ FetchingRecordsToSave = false;
1364
+ /** @deprecated Use {@link FetchingRecordsToSave}. */
1365
+ get fetchingRecordsToSave() {
1366
+ return this.FetchingRecordsToSave;
1367
+ }
1368
+ /** @deprecated Use {@link FetchingRecordsToSave}. */
1369
+ set fetchingRecordsToSave(value) {
1370
+ this.FetchingRecordsToSave = value;
1371
+ }
948
1372
  // Dropdown button toggle state
949
- showAddDropdown = false;
1373
+ ShowAddDropdown = false;
1374
+ /** @deprecated Use {@link ShowAddDropdown}. */
1375
+ get showAddDropdown() {
1376
+ return this.ShowAddDropdown;
1377
+ }
1378
+ /** @deprecated Use {@link ShowAddDropdown}. */
1379
+ set showAddDropdown(value) {
1380
+ this.ShowAddDropdown = value;
1381
+ }
950
1382
  // Dropdown menu options
951
- addOptions = [
1383
+ AddOptions = [
952
1384
  {
953
1385
  Text: 'Add Records',
954
1386
  Description: 'Search and add specific records to this list',
955
1387
  Icon: 'search',
956
- Action: () => this.openAddRecordsDialog()
1388
+ Action: () => this.OpenAddRecordsDialog()
957
1389
  },
958
1390
  {
959
1391
  Text: 'Add From View',
960
1392
  Description: 'Add all records from a saved view',
961
1393
  Icon: 'folder',
962
- Action: () => this.openAddFromViewDialog()
1394
+ Action: () => this.OpenAddFromViewDialog()
963
1395
  }
964
1396
  ];
1397
+ /** @deprecated Use {@link AddOptions}. */
1398
+ get addOptions() {
1399
+ return this.AddOptions;
1400
+ }
1401
+ /** @deprecated Use {@link AddOptions}. */
1402
+ set addOptions(value) {
1403
+ this.AddOptions = value;
1404
+ }
965
1405
  onDocumentClick(event) {
966
- if (this.showAddDropdown) {
1406
+ if (this.ShowAddDropdown) {
967
1407
  const target = event.target;
968
1408
  if (!this.elementRef.nativeElement.querySelector('.add-dropdown-wrapper')?.contains(target)) {
969
- this.showAddDropdown = false;
1409
+ this.ShowAddDropdown = false;
970
1410
  }
971
1411
  }
972
1412
  }
@@ -1002,7 +1442,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1002
1442
  async loadListRecord() {
1003
1443
  if (!this.ListID)
1004
1444
  return;
1005
- this.showLoader = true;
1445
+ this.ShowLoader = true;
1006
1446
  try {
1007
1447
  const md = this.ProviderToUse;
1008
1448
  // Build + load against a LOCAL handle. Only assign to
@@ -1014,20 +1454,20 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1014
1454
  const loadResult = await list.Load(this.ListID);
1015
1455
  if (!loadResult) {
1016
1456
  LogError("Error loading list with ID " + this.ListID, undefined, list.LatestResult);
1017
- this.listRecord = null;
1457
+ this.ListRecord = null;
1018
1458
  }
1019
1459
  else {
1020
- this.listRecord = list;
1460
+ this.ListRecord = list;
1021
1461
  await this.loadLineageContext();
1022
1462
  await this.loadCapabilities();
1023
1463
  }
1024
1464
  }
1025
1465
  catch (error) {
1026
1466
  LogError("Error loading list", undefined, error);
1027
- this.listRecord = null;
1467
+ this.ListRecord = null;
1028
1468
  }
1029
1469
  finally {
1030
- this.showLoader = false;
1470
+ this.ShowLoader = false;
1031
1471
  this.cdr.detectChanges();
1032
1472
  }
1033
1473
  }
@@ -1040,19 +1480,19 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1040
1480
  * rather than blocking the rest of the detail view.
1041
1481
  */
1042
1482
  async loadLineageContext() {
1043
- if (!this.listRecord?.SourceViewID) {
1044
- this.sourceViewName = null;
1483
+ if (!this.ListRecord?.SourceViewID) {
1484
+ this.SourceViewName = null;
1045
1485
  return;
1046
1486
  }
1047
- this.refreshMode = this.loadLastUsedMode() ?? this.listRecord.RefreshMode;
1487
+ this.RefreshMode = this.loadLastUsedMode() ?? this.ListRecord.RefreshMode;
1048
1488
  try {
1049
1489
  const view = await this.ProviderToUse.GetEntityObject('MJ: User Views');
1050
- const loaded = await view.Load(this.listRecord.SourceViewID);
1051
- this.sourceViewName = loaded ? view.Name : null;
1490
+ const loaded = await view.Load(this.ListRecord.SourceViewID);
1491
+ this.SourceViewName = loaded ? view.Name : null;
1052
1492
  }
1053
1493
  catch (e) {
1054
1494
  LogError(`Failed to load source view name for list ${this.ListID}: ${e}`);
1055
- this.sourceViewName = null;
1495
+ this.SourceViewName = null;
1056
1496
  }
1057
1497
  }
1058
1498
  /**
@@ -1062,16 +1502,16 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1062
1502
  * so we don't accidentally surface buttons the server will reject.
1063
1503
  */
1064
1504
  async loadCapabilities() {
1065
- if (!this.listRecord) {
1066
- this.capabilities = CapabilitiesForLevel(null);
1067
- this.currentLevel = null;
1505
+ if (!this.ListRecord) {
1506
+ this.Capabilities = CapabilitiesForLevel(null);
1507
+ this.CurrentLevel = null;
1068
1508
  return;
1069
1509
  }
1070
1510
  try {
1071
1511
  const currentUserId = this.ProviderToUse.CurrentUser?.ID;
1072
1512
  if (!currentUserId) {
1073
- this.capabilities = CapabilitiesForLevel('View');
1074
- this.currentLevel = 'View';
1513
+ this.Capabilities = CapabilitiesForLevel('View');
1514
+ this.CurrentLevel = 'View';
1075
1515
  return;
1076
1516
  }
1077
1517
  // Fast path: the list's UserID is its owner. Owners always have full
@@ -1079,25 +1519,29 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1079
1519
  // is implicit), so the permission-row lookup below would return null
1080
1520
  // and incorrectly hide all edit/share/delete buttons. Mirrors the
1081
1521
  // owner short-circuit in the server-side ListSharing.ResolveEffectivePermission.
1082
- if (UUIDsEqual(this.listRecord.UserID, currentUserId)) {
1083
- this.currentLevel = 'Owner';
1084
- this.capabilities = CapabilitiesForLevel('Owner');
1522
+ if (UUIDsEqual(this.ListRecord.UserID, currentUserId)) {
1523
+ this.CurrentLevel = 'Owner';
1524
+ this.Capabilities = CapabilitiesForLevel('Owner');
1085
1525
  return;
1086
1526
  }
1087
1527
  // Non-owner: resolve via ListSharingService (GraphQL). Never instantiate
1088
1528
  // the server-side `ListSharing` class from a browser bundle.
1089
- const level = (await this.listSharingService.getUserPermissionLevel(this.listRecord.ID, currentUserId));
1090
- this.currentLevel = level;
1091
- this.capabilities = CapabilitiesForLevel(level);
1529
+ const level = (await this.listSharingService.getUserPermissionLevel(this.ListRecord.ID, currentUserId));
1530
+ this.CurrentLevel = level;
1531
+ this.Capabilities = CapabilitiesForLevel(level);
1092
1532
  }
1093
1533
  catch (e) {
1094
1534
  LogError(`loadCapabilities failed: ${e instanceof Error ? e.message : String(e)}`);
1095
- this.capabilities = CapabilitiesForLevel('View');
1096
- this.currentLevel = 'View';
1535
+ this.Capabilities = CapabilitiesForLevel('View');
1536
+ this.CurrentLevel = 'View';
1097
1537
  }
1098
1538
  }
1539
+ get HasLineage() {
1540
+ return !!this.ListRecord?.SourceViewID;
1541
+ }
1542
+ /** @deprecated Use {@link HasLineage}. */
1099
1543
  get hasLineage() {
1100
- return !!this.listRecord?.SourceViewID;
1544
+ return this.HasLineage;
1101
1545
  }
1102
1546
  loadLastUsedMode() {
1103
1547
  try {
@@ -1123,57 +1567,65 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1123
1567
  * and opens the confirm dialog when it returns. The dialog enforces the
1124
1568
  * acknowledgement UX; the server enforces the actual drop guard.
1125
1569
  */
1126
- async onRefreshFromSource() {
1127
- if (!this.hasLineage || this.isPreviewingRefresh)
1570
+ async OnRefreshFromSource() {
1571
+ if (!this.HasLineage || this.IsPreviewingRefresh)
1128
1572
  return;
1129
- this.isPreviewingRefresh = true;
1130
- this.refreshDelta = null;
1573
+ this.IsPreviewingRefresh = true;
1574
+ this.RefreshDelta = null;
1131
1575
  this.cdr.detectChanges();
1132
1576
  try {
1133
1577
  const provider = this.ProviderToUse;
1134
1578
  const client = new GraphQLListsClient(provider);
1135
1579
  const delta = await client.PreviewListDelta({
1136
1580
  Target: this.ListID,
1137
- Source: { kind: 'view', viewId: this.listRecord.SourceViewID },
1138
- Mode: this.refreshMode,
1581
+ Source: { kind: 'view', viewId: this.ListRecord.SourceViewID },
1582
+ Mode: this.RefreshMode,
1139
1583
  });
1140
- this.refreshDelta = delta;
1141
- this.refreshConfirmVisible = true;
1584
+ this.RefreshDelta = delta;
1585
+ this.RefreshConfirmVisible = true;
1142
1586
  }
1143
1587
  catch (e) {
1144
1588
  const message = e instanceof Error ? e.message : String(e);
1145
1589
  this.sharedService.CreateSimpleNotification(`Refresh preview failed: ${message}`, 'error', 5000);
1146
1590
  }
1147
1591
  finally {
1148
- this.isPreviewingRefresh = false;
1592
+ this.IsPreviewingRefresh = false;
1149
1593
  this.cdr.detectChanges();
1150
1594
  }
1151
1595
  }
1152
- onRefreshConfirmCancel() {
1153
- this.refreshConfirmVisible = false;
1154
- this.refreshDelta = null;
1596
+ /** @deprecated Use {@link OnRefreshFromSource}. */
1597
+ async onRefreshFromSource() {
1598
+ return this.OnRefreshFromSource();
1599
+ }
1600
+ OnRefreshConfirmCancel() {
1601
+ this.RefreshConfirmVisible = false;
1602
+ this.RefreshDelta = null;
1155
1603
  this.cdr.detectChanges();
1156
1604
  }
1157
- async onRefreshConfirmCommit(deltaToken) {
1158
- if (!this.refreshDelta)
1605
+ /** @deprecated Use {@link OnRefreshConfirmCancel}. */
1606
+ onRefreshConfirmCancel() {
1607
+ return this.OnRefreshConfirmCancel();
1608
+ }
1609
+ async OnRefreshConfirmCommit(deltaToken) {
1610
+ if (!this.RefreshDelta)
1159
1611
  return;
1160
- this.isApplyingRefresh = true;
1612
+ this.IsApplyingRefresh = true;
1161
1613
  this.cdr.detectChanges();
1162
1614
  try {
1163
1615
  const provider = this.ProviderToUse;
1164
1616
  const client = new GraphQLListsClient(provider);
1165
1617
  const result = await client.ApplyListDelta({
1166
- Delta: { ...this.refreshDelta, DeltaToken: deltaToken },
1167
- ConfirmDrops: (this.refreshDelta.Counts.Remove ?? 0) > 0,
1618
+ Delta: { ...this.RefreshDelta, DeltaToken: deltaToken },
1619
+ ConfirmDrops: (this.RefreshDelta.Counts.Remove ?? 0) > 0,
1168
1620
  });
1169
1621
  if (result.Success) {
1170
- this.saveLastUsedMode(this.refreshMode);
1171
- this.refreshConfirmVisible = false;
1172
- this.refreshDelta = null;
1622
+ this.saveLastUsedMode(this.RefreshMode);
1623
+ this.RefreshConfirmVisible = false;
1624
+ this.RefreshDelta = null;
1173
1625
  this.sharedService.CreateSimpleNotification(`Refresh applied: +${result.Counts?.Added ?? 0} / -${result.Counts?.Removed ?? 0}`, 'success', 3000);
1174
1626
  // Reload list (for LastRefreshedAt) + grid in parallel.
1175
1627
  await this.loadListRecord();
1176
- this.refreshGrid();
1628
+ this.RefreshGrid();
1177
1629
  this.bumpStatsRefresh();
1178
1630
  }
1179
1631
  else {
@@ -1185,59 +1637,103 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1185
1637
  this.sharedService.CreateSimpleNotification(`Refresh failed: ${message}`, 'error', 5000);
1186
1638
  }
1187
1639
  finally {
1188
- this.isApplyingRefresh = false;
1640
+ this.IsApplyingRefresh = false;
1189
1641
  this.cdr.detectChanges();
1190
1642
  }
1191
1643
  }
1644
+ /** @deprecated Use {@link OnRefreshConfirmCommit}. */
1645
+ async onRefreshConfirmCommit(deltaToken) {
1646
+ return this.OnRefreshConfirmCommit(deltaToken);
1647
+ }
1648
+ OnRefreshModeChange(mode) {
1649
+ this.RefreshMode = mode;
1650
+ }
1651
+ /** @deprecated Use {@link OnRefreshModeChange}. */
1192
1652
  onRefreshModeChange(mode) {
1193
- this.refreshMode = mode;
1653
+ return this.OnRefreshModeChange(mode);
1194
1654
  }
1195
1655
  // ==========================================
1196
1656
  // Grid Event Handlers
1197
1657
  // ==========================================
1198
- onRowClicked(_event) {
1658
+ OnRowClicked(_event) {
1199
1659
  // Selection is handled by the grid
1200
1660
  }
1201
- onRowDoubleClicked(_event) {
1661
+ /** @deprecated Use {@link OnRowClicked}. */
1662
+ onRowClicked(_event) {
1663
+ return this.OnRowClicked(_event);
1664
+ }
1665
+ OnRowDoubleClicked(_event) {
1202
1666
  // Navigation is handled by mj-list-detail-grid
1203
1667
  }
1668
+ /** @deprecated Use {@link OnRowDoubleClicked}. */
1669
+ onRowDoubleClicked(_event) {
1670
+ return this.OnRowDoubleClicked(_event);
1671
+ }
1672
+ OnSelectionChange(keys) {
1673
+ this.SelectedKeys = keys;
1674
+ }
1675
+ /** @deprecated Use {@link OnSelectionChange}. */
1204
1676
  onSelectionChange(keys) {
1205
- this.selectedKeys = keys;
1677
+ return this.OnSelectionChange(keys);
1678
+ }
1679
+ OnDataLoaded(event) {
1680
+ this.RowCount = event.totalCount;
1206
1681
  }
1682
+ /** @deprecated Use {@link OnDataLoaded}. */
1207
1683
  onDataLoaded(event) {
1208
- this.rowCount = event.totalCount;
1684
+ return this.OnDataLoaded(event);
1209
1685
  }
1210
- refreshGrid() {
1211
- if (this.listDetailGrid) {
1212
- this.listDetailGrid.refresh();
1686
+ RefreshGrid() {
1687
+ if (this.ListDetailGrid) {
1688
+ this.ListDetailGrid.refresh();
1213
1689
  }
1214
1690
  }
1691
+ /** @deprecated Use {@link RefreshGrid}. */
1692
+ refreshGrid() {
1693
+ return this.RefreshGrid();
1694
+ }
1215
1695
  // ==========================================
1216
1696
  // Toolbar Actions
1217
1697
  // ==========================================
1218
1698
  // ==========================================
1219
1699
  // Progress Percentage Getters
1220
1700
  // ==========================================
1701
+ get RemoveProgressPercent() {
1702
+ return this.RemoveTotal > 0 ? Math.round((this.RemoveProgress / this.RemoveTotal) * 100) : 0;
1703
+ }
1704
+ /** @deprecated Use {@link RemoveProgressPercent}. */
1221
1705
  get removeProgressPercent() {
1222
- return this.removeTotal > 0 ? Math.round((this.removeProgress / this.removeTotal) * 100) : 0;
1706
+ return this.RemoveProgressPercent;
1707
+ }
1708
+ get AddProgressPercent() {
1709
+ return this.AddTotal > 0 ? Math.round((this.AddProgress / this.AddTotal) * 100) : 0;
1223
1710
  }
1711
+ /** @deprecated Use {@link AddProgressPercent}. */
1224
1712
  get addProgressPercent() {
1225
- return this.addTotal > 0 ? Math.round((this.addProgress / this.addTotal) * 100) : 0;
1713
+ return this.AddProgressPercent;
1226
1714
  }
1227
1715
  /** Update progress + recompute the stored percent atomically. Call this
1228
1716
  * from inside the save loop so the bound value is set in lockstep with
1229
1717
  * the underlying counter (avoids NG0100). */
1230
1718
  setAddFromViewProgress(progress) {
1231
- this.addFromViewProgress = progress;
1232
- this.addFromViewProgressPercent = this.addFromViewTotal > 0
1233
- ? Math.round((progress / this.addFromViewTotal) * 100)
1719
+ this.AddFromViewProgress = progress;
1720
+ this.AddFromViewProgressPercent = this.AddFromViewTotal > 0
1721
+ ? Math.round((progress / this.AddFromViewTotal) * 100)
1234
1722
  : 0;
1235
1723
  }
1724
+ OnRefreshClick() {
1725
+ this.RefreshGrid();
1726
+ }
1727
+ /** @deprecated Use {@link OnRefreshClick}. */
1236
1728
  onRefreshClick() {
1237
- this.refreshGrid();
1729
+ return this.OnRefreshClick();
1730
+ }
1731
+ OnExportClick() {
1732
+ this.OpenExportDialog();
1238
1733
  }
1734
+ /** @deprecated Use {@link OnExportClick}. */
1239
1735
  onExportClick() {
1240
- this.openExportDialog();
1736
+ return this.OnExportClick();
1241
1737
  }
1242
1738
  /**
1243
1739
  * Open the format + column picker (mockup 26). Resolves the candidate
@@ -1245,13 +1741,13 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1245
1741
  * RunView. Default selection is every non-virtual entity field, which
1246
1742
  * matches what the underlying grid's "export all" path produced.
1247
1743
  */
1248
- openExportDialog() {
1249
- if (!this.listRecord) {
1744
+ OpenExportDialog() {
1745
+ if (!this.ListRecord) {
1250
1746
  this.sharedService.CreateSimpleNotification('Load a list first before exporting.', 'info', 3000);
1251
1747
  return;
1252
1748
  }
1253
1749
  const md = this.ProviderToUse;
1254
- const entityInfo = md.EntityByID(this.listRecord.EntityID);
1750
+ const entityInfo = md.EntityByID(this.ListRecord.EntityID);
1255
1751
  if (!entityInfo) {
1256
1752
  this.sharedService.CreateSimpleNotification(`Entity for this list not found in metadata.`, 'error', 4000);
1257
1753
  return;
@@ -1260,32 +1756,52 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1260
1756
  this.sharedService.CreateSimpleNotification(`Composite-PK entities ('${entityInfo.Name}') aren't yet supported for List export.`, 'warning', 5000);
1261
1757
  return;
1262
1758
  }
1263
- this.exportFields = entityInfo.Fields
1759
+ this.ExportFields = entityInfo.Fields
1264
1760
  .filter((f) => f.IsVirtual !== true)
1265
1761
  .map((f) => ({
1266
1762
  Name: f.Name,
1267
1763
  DisplayName: f.DisplayName || f.Name,
1268
1764
  Selected: true,
1269
1765
  }));
1270
- this.exportRecordCount = this.rowCount;
1271
- this.exportFormat = 'excel';
1272
- this.showExportDialog = true;
1766
+ this.ExportRecordCount = this.RowCount;
1767
+ this.ExportFormat = 'excel';
1768
+ this.ShowExportDialog = true;
1273
1769
  this.cdr.detectChanges();
1274
1770
  }
1275
- closeExportDialog() {
1276
- this.showExportDialog = false;
1771
+ /** @deprecated Use {@link OpenExportDialog}. */
1772
+ openExportDialog() {
1773
+ return this.OpenExportDialog();
1774
+ }
1775
+ CloseExportDialog() {
1776
+ this.ShowExportDialog = false;
1277
1777
  this.cdr.detectChanges();
1278
1778
  }
1279
- selectAllExportFields() {
1280
- for (const f of this.exportFields)
1779
+ /** @deprecated Use {@link CloseExportDialog}. */
1780
+ closeExportDialog() {
1781
+ return this.CloseExportDialog();
1782
+ }
1783
+ SelectAllExportFields() {
1784
+ for (const f of this.ExportFields)
1281
1785
  f.Selected = true;
1282
1786
  }
1283
- selectNoneExportFields() {
1284
- for (const f of this.exportFields)
1787
+ /** @deprecated Use {@link SelectAllExportFields}. */
1788
+ selectAllExportFields() {
1789
+ return this.SelectAllExportFields();
1790
+ }
1791
+ SelectNoneExportFields() {
1792
+ for (const f of this.ExportFields)
1285
1793
  f.Selected = false;
1286
1794
  }
1795
+ /** @deprecated Use {@link SelectNoneExportFields}. */
1796
+ selectNoneExportFields() {
1797
+ return this.SelectNoneExportFields();
1798
+ }
1799
+ get SelectedExportFieldCount() {
1800
+ return this.ExportFields.filter((f) => f.Selected).length;
1801
+ }
1802
+ /** @deprecated Use {@link SelectedExportFieldCount}. */
1287
1803
  get selectedExportFieldCount() {
1288
- return this.exportFields.filter((f) => f.Selected).length;
1804
+ return this.SelectedExportFieldCount;
1289
1805
  }
1290
1806
  /**
1291
1807
  * Run the export with the user's chosen format + columns. Resolves
@@ -1294,27 +1810,27 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1294
1810
  * rows restricted to the chosen Fields. Output is projected to
1295
1811
  * exactly the user's selected columns + ordering.
1296
1812
  */
1297
- async executeExport() {
1298
- if (!this.listRecord)
1813
+ async ExecuteExport() {
1814
+ if (!this.ListRecord)
1299
1815
  return;
1300
- const selectedFields = this.exportFields.filter((f) => f.Selected).map((f) => f.Name);
1816
+ const selectedFields = this.ExportFields.filter((f) => f.Selected).map((f) => f.Name);
1301
1817
  if (selectedFields.length === 0)
1302
1818
  return;
1303
- this.isExporting = true;
1819
+ this.IsExporting = true;
1304
1820
  this.cdr.detectChanges();
1305
1821
  try {
1306
1822
  const md = this.ProviderToUse;
1307
- const entityInfo = md.EntityByID(this.listRecord.EntityID);
1823
+ const entityInfo = md.EntityByID(this.ListRecord.EntityID);
1308
1824
  // Cheap emptiness check before doing any row work
1309
1825
  const rv = RunView.FromMetadataProvider(md);
1310
1826
  const countResult = await rv.RunView({
1311
1827
  EntityName: 'MJ: List Details',
1312
- ExtraFilter: `ListID='${this.listRecord.ID}'`,
1828
+ ExtraFilter: `ListID='${this.ListRecord.ID}'`,
1313
1829
  ResultType: 'count_only',
1314
1830
  });
1315
1831
  if (countResult.Success && countResult.TotalRowCount === 0) {
1316
1832
  this.sharedService.CreateSimpleNotification('List is empty — nothing to export.', 'info', 3000);
1317
- this.showExportDialog = false;
1833
+ this.ShowExportDialog = false;
1318
1834
  return;
1319
1835
  }
1320
1836
  // Pull underlying entity rows restricted to the chosen fields, with
@@ -1324,7 +1840,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1324
1840
  const fieldsForQuery = Array.from(new Set([...entityInfo.PrimaryKeys.map((pk) => pk.Name), ...selectedFields]));
1325
1841
  const rowResult = await rv.RunView({
1326
1842
  EntityName: entityInfo.Name,
1327
- ExtraFilter: await this.buildListMemberFilter(entityInfo, this.listRecord.ID, rv),
1843
+ ExtraFilter: await this.buildListMemberFilter(entityInfo, this.ListRecord.ID, rv),
1328
1844
  Fields: fieldsForQuery,
1329
1845
  ResultType: 'simple',
1330
1846
  });
@@ -1340,18 +1856,18 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1340
1856
  return projected;
1341
1857
  });
1342
1858
  const dateStamp = new Date().toISOString().slice(0, 10);
1343
- const safeName = (this.listRecord.Name || 'list').replace(/[^a-z0-9_-]+/gi, '_');
1344
- const ext = this.exportFormat === 'excel' ? 'xlsx' : this.exportFormat;
1859
+ const safeName = (this.ListRecord.Name || 'list').replace(/[^a-z0-9_-]+/gi, '_');
1860
+ const ext = this.ExportFormat === 'excel' ? 'xlsx' : this.ExportFormat;
1345
1861
  const fileName = `${safeName}-${dateStamp}.${ext}`;
1346
- const exportResult = this.exportFormat === 'excel'
1862
+ const exportResult = this.ExportFormat === 'excel'
1347
1863
  ? await this.exportService.toExcel(rows, { fileName, includeHeaders: true })
1348
- : this.exportFormat === 'csv'
1864
+ : this.ExportFormat === 'csv'
1349
1865
  ? await this.exportService.toCSV(rows, { fileName, includeHeaders: true })
1350
1866
  : await this.exportService.toJSON(rows, { fileName });
1351
1867
  if (exportResult.success) {
1352
1868
  this.exportService.downloadResult(exportResult);
1353
1869
  this.sharedService.CreateSimpleNotification(`Exported ${rows.length} record(s)`, 'success', 3000);
1354
- this.showExportDialog = false;
1870
+ this.ShowExportDialog = false;
1355
1871
  }
1356
1872
  else {
1357
1873
  this.sharedService.CreateSimpleNotification('Export failed', 'error', 5000);
@@ -1362,19 +1878,31 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1362
1878
  this.sharedService.CreateSimpleNotification(`Export error: ${message}`, 'error', 5000);
1363
1879
  }
1364
1880
  finally {
1365
- this.isExporting = false;
1881
+ this.IsExporting = false;
1366
1882
  this.cdr.detectChanges();
1367
1883
  }
1368
1884
  }
1885
+ /** @deprecated Use {@link ExecuteExport}. */
1886
+ async executeExport() {
1887
+ return this.ExecuteExport();
1888
+ }
1889
+ ToggleAddDropdown() {
1890
+ this.ShowAddDropdown = !this.ShowAddDropdown;
1891
+ }
1892
+ /** @deprecated Use {@link ToggleAddDropdown}. */
1369
1893
  toggleAddDropdown() {
1370
- this.showAddDropdown = !this.showAddDropdown;
1894
+ return this.ToggleAddDropdown();
1371
1895
  }
1372
- onDropdownItemClick(item) {
1373
- this.showAddDropdown = false;
1896
+ OnDropdownItemClick(item) {
1897
+ this.ShowAddDropdown = false;
1374
1898
  if (item.Action) {
1375
1899
  item.Action();
1376
1900
  }
1377
1901
  }
1902
+ /** @deprecated Use {@link OnDropdownItemClick}. */
1903
+ onDropdownItemClick(item) {
1904
+ return this.OnDropdownItemClick(item);
1905
+ }
1378
1906
  // ==========================================
1379
1907
  /**
1380
1908
  * Predicate selecting the underlying entity rows that are members of a list. For a
@@ -1404,16 +1932,16 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1404
1932
  * the existing extract-record-id-from-composite-key logic to map
1405
1933
  * `selectedKeys` to the actual `MJ: List Detail.RecordID` values.
1406
1934
  */
1407
- async applyBulkStatus() {
1408
- if (!this.listRecord || this.selectedKeys.length === 0 || !this.bulkStatus)
1935
+ async ApplyBulkStatus() {
1936
+ if (!this.ListRecord || this.SelectedKeys.length === 0 || !this.BulkStatus)
1409
1937
  return;
1410
- this.isApplyingBulkStatus = true;
1938
+ this.IsApplyingBulkStatus = true;
1411
1939
  this.cdr.detectChanges();
1412
1940
  try {
1413
1941
  const md = this.ProviderToUse;
1414
1942
  const rv = RunView.FromMetadataProvider(md);
1415
- const entityInfo = md.EntityByID(this.listRecord.EntityID);
1416
- const recordIds = this.selectedKeys.map((key) => {
1943
+ const entityInfo = md.EntityByID(this.ListRecord.EntityID);
1944
+ const recordIds = this.SelectedKeys.map((key) => {
1417
1945
  if (entityInfo && entityInfo.PrimaryKeys.length === 1) {
1418
1946
  const ck = new CompositeKey();
1419
1947
  ck.LoadFromConcatenatedString(key);
@@ -1421,7 +1949,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1421
1949
  }
1422
1950
  return key;
1423
1951
  });
1424
- const filter = `ListID='${this.listRecord.ID}' AND RecordID IN (${recordIds.map((id) => `'${String(id).replace(/'/g, "''")}'`).join(',')})`;
1952
+ const filter = `ListID='${this.ListRecord.ID}' AND RecordID IN (${recordIds.map((id) => `'${String(id).replace(/'/g, "''")}'`).join(',')})`;
1425
1953
  // Two-step: fetch just the IDs via a 'simple' RunView, then load each
1426
1954
  // entity through GetEntityObject(..., CurrentUser) so the entity is
1427
1955
  // born with the user context that Save() requires. RunView with
@@ -1451,7 +1979,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1451
1979
  failureMessages.push(reason);
1452
1980
  continue;
1453
1981
  }
1454
- detail.Status = this.bulkStatus;
1982
+ detail.Status = this.BulkStatus;
1455
1983
  const ok = await detail.Save();
1456
1984
  if (ok) {
1457
1985
  updated++;
@@ -1465,14 +1993,14 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1465
1993
  }
1466
1994
  }
1467
1995
  this.sharedService.CreateSimpleNotification(failed === 0
1468
- ? `Updated ${updated} item(s) to '${this.bulkStatus}'`
1996
+ ? `Updated ${updated} item(s) to '${this.BulkStatus}'`
1469
1997
  : `Updated ${updated}, ${failed} failed: ${failureMessages.join(' | ')}`, failed === 0 ? 'success' : 'warning', failed === 0 ? 3000 : 8000);
1470
- this.bulkStatus = '';
1998
+ this.BulkStatus = '';
1471
1999
  // Defer the grid refresh to the next microtask — see comment in
1472
2000
  // confirmRemoveFromList for why running it synchronously here
1473
2001
  // triggers NG0100 and silently breaks the UI refresh.
1474
2002
  await Promise.resolve();
1475
- this.refreshGrid();
2003
+ this.RefreshGrid();
1476
2004
  this.bumpStatsRefresh();
1477
2005
  }
1478
2006
  catch (e) {
@@ -1480,10 +2008,14 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1480
2008
  this.sharedService.CreateSimpleNotification(`Bulk update failed: ${message}`, 'error', 5000);
1481
2009
  }
1482
2010
  finally {
1483
- this.isApplyingBulkStatus = false;
2011
+ this.IsApplyingBulkStatus = false;
1484
2012
  this.cdr.detectChanges();
1485
2013
  }
1486
2014
  }
2015
+ /** @deprecated Use {@link ApplyBulkStatus}. */
2016
+ async applyBulkStatus() {
2017
+ return this.ApplyBulkStatus();
2018
+ }
1487
2019
  // ==========================================
1488
2020
  // Bulk Move / Copy (mockups 23, 24)
1489
2021
  //
@@ -1497,17 +2029,25 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1497
2029
  // needed for the preview. Apply step uses direct entity Save/Delete
1498
2030
  // inside a single TransactionGroup (same pattern as Remove).
1499
2031
  // ==========================================
1500
- openMoveDialog() { this.openMoveCopyDialog('move'); }
1501
- openCopyDialog() { this.openMoveCopyDialog('copy'); }
2032
+ OpenMoveDialog() { this.openMoveCopyDialog('move'); }
2033
+ /** @deprecated Use {@link OpenMoveDialog}. */
2034
+ openMoveDialog() {
2035
+ return this.OpenMoveDialog();
2036
+ }
2037
+ OpenCopyDialog() { this.openMoveCopyDialog('copy'); }
2038
+ /** @deprecated Use {@link OpenCopyDialog}. */
2039
+ openCopyDialog() {
2040
+ return this.OpenCopyDialog();
2041
+ }
1502
2042
  openMoveCopyDialog(mode) {
1503
- if (!this.listRecord || this.selectedKeys.length === 0) {
2043
+ if (!this.ListRecord || this.SelectedKeys.length === 0) {
1504
2044
  this.sharedService.CreateSimpleNotification('Please select records first', 'warning', 2500);
1505
2045
  return;
1506
2046
  }
1507
- this.moveCopyMode = mode;
1508
- this.moveCopySelectedTarget = null;
1509
- this.moveCopyTargetSearch = '';
1510
- this.showMoveCopyDialog = true;
2047
+ this.MoveCopyMode = mode;
2048
+ this.MoveCopySelectedTarget = null;
2049
+ this.MoveCopyTargetSearch = '';
2050
+ this.ShowMoveCopyDialog = true;
1511
2051
  // Load candidates once per dialog open. We don't keep them in
1512
2052
  // permanent component state because the user could create/delete
1513
2053
  // lists between opens, and the target picker isn't visible often
@@ -1515,32 +2055,48 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1515
2055
  void this.loadMoveCopyTargets();
1516
2056
  this.cdr.detectChanges();
1517
2057
  }
1518
- closeMoveCopyDialog() {
1519
- this.showMoveCopyDialog = false;
1520
- this.moveCopyTargetCandidates = [];
1521
- this.moveCopySelectedTarget = null;
2058
+ CloseMoveCopyDialog() {
2059
+ this.ShowMoveCopyDialog = false;
2060
+ this.MoveCopyTargetCandidates = [];
2061
+ this.MoveCopySelectedTarget = null;
1522
2062
  this.cdr.detectChanges();
1523
2063
  }
2064
+ /** @deprecated Use {@link CloseMoveCopyDialog}. */
2065
+ closeMoveCopyDialog() {
2066
+ return this.CloseMoveCopyDialog();
2067
+ }
2068
+ SelectMoveCopyTarget(target) {
2069
+ this.MoveCopySelectedTarget = target;
2070
+ }
2071
+ /** @deprecated Use {@link SelectMoveCopyTarget}. */
1524
2072
  selectMoveCopyTarget(target) {
1525
- this.moveCopySelectedTarget = target;
2073
+ return this.SelectMoveCopyTarget(target);
1526
2074
  }
1527
- get filteredMoveCopyTargets() {
1528
- const term = this.moveCopyTargetSearch.trim().toLowerCase();
2075
+ get FilteredMoveCopyTargets() {
2076
+ const term = this.MoveCopyTargetSearch.trim().toLowerCase();
1529
2077
  if (!term)
1530
- return this.moveCopyTargetCandidates;
1531
- return this.moveCopyTargetCandidates.filter((l) => l.Name.toLowerCase().includes(term) ||
2078
+ return this.MoveCopyTargetCandidates;
2079
+ return this.MoveCopyTargetCandidates.filter((l) => l.Name.toLowerCase().includes(term) ||
1532
2080
  (l.Description?.toLowerCase().includes(term) ?? false));
1533
2081
  }
2082
+ /** @deprecated Use {@link FilteredMoveCopyTargets}. */
2083
+ get filteredMoveCopyTargets() {
2084
+ return this.FilteredMoveCopyTargets;
2085
+ }
2086
+ get MoveCopyProgressPercent() {
2087
+ return this.MoveCopyTotal > 0 ? Math.round((this.MoveCopyProgress / this.MoveCopyTotal) * 100) : 0;
2088
+ }
2089
+ /** @deprecated Use {@link MoveCopyProgressPercent}. */
1534
2090
  get moveCopyProgressPercent() {
1535
- return this.moveCopyTotal > 0 ? Math.round((this.moveCopyProgress / this.moveCopyTotal) * 100) : 0;
2091
+ return this.MoveCopyProgressPercent;
1536
2092
  }
1537
2093
  /** Load candidate target lists — same Entity, owned by or shared with
1538
2094
  * the current user, excluding the list we're standing on. One RunView
1539
2095
  * on open; results stay in memory until the dialog closes. */
1540
2096
  async loadMoveCopyTargets() {
1541
- if (!this.listRecord)
2097
+ if (!this.ListRecord)
1542
2098
  return;
1543
- this.moveCopyTargetCandidatesLoading = true;
2099
+ this.MoveCopyTargetCandidatesLoading = true;
1544
2100
  this.cdr.detectChanges();
1545
2101
  try {
1546
2102
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
@@ -1548,7 +2104,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1548
2104
  // would over-restrict (Shared-With-Me lists should be valid targets
1549
2105
  // when the user has Edit there); the server enforces the real
1550
2106
  // permission on the per-row Save, so listing extra entries is fine.
1551
- const filter = `EntityID='${this.listRecord.EntityID}' AND ID<>'${this.listRecord.ID}'`;
2107
+ const filter = `EntityID='${this.ListRecord.EntityID}' AND ID<>'${this.ListRecord.ID}'`;
1552
2108
  const result = await rv.RunView({
1553
2109
  EntityName: 'MJ: Lists',
1554
2110
  ExtraFilter: filter,
@@ -1557,20 +2113,20 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1557
2113
  MaxRows: 500,
1558
2114
  });
1559
2115
  if (result.Success) {
1560
- this.moveCopyTargetCandidates = (result.Results ?? []);
2116
+ this.MoveCopyTargetCandidates = (result.Results ?? []);
1561
2117
  }
1562
2118
  else {
1563
- this.moveCopyTargetCandidates = [];
2119
+ this.MoveCopyTargetCandidates = [];
1564
2120
  this.sharedService.CreateSimpleNotification(`Failed to load target lists: ${result.ErrorMessage}`, 'error', 4000);
1565
2121
  }
1566
2122
  }
1567
2123
  catch (e) {
1568
- this.moveCopyTargetCandidates = [];
2124
+ this.MoveCopyTargetCandidates = [];
1569
2125
  const message = e instanceof Error ? e.message : String(e);
1570
2126
  this.sharedService.CreateSimpleNotification(`Failed to load target lists: ${message}`, 'error', 4000);
1571
2127
  }
1572
2128
  finally {
1573
- this.moveCopyTargetCandidatesLoading = false;
2129
+ this.MoveCopyTargetCandidatesLoading = false;
1574
2130
  this.cdr.detectChanges();
1575
2131
  }
1576
2132
  }
@@ -1579,33 +2135,45 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1579
2135
  * immediately. For Move we build a local Delta and show the confirm
1580
2136
  * dialog — the actual mutation fires from onMoveConfirmCommit().
1581
2137
  */
1582
- async confirmMoveCopy() {
1583
- if (!this.listRecord || !this.moveCopySelectedTarget || this.selectedKeys.length === 0)
2138
+ async ConfirmMoveCopy() {
2139
+ if (!this.ListRecord || !this.MoveCopySelectedTarget || this.SelectedKeys.length === 0)
1584
2140
  return;
1585
- if (this.moveCopyMode === 'copy') {
2141
+ if (this.MoveCopyMode === 'copy') {
1586
2142
  await this.applyMoveCopy(false);
1587
2143
  return;
1588
2144
  }
1589
2145
  // Move: show delta-confirm so the user explicitly acks the source-side drop.
1590
2146
  const recordIds = this.extractSelectedRecordIds();
1591
- this.moveDelta = this.buildMoveDelta(recordIds);
1592
- this.showMoveCopyDialog = false;
1593
- this.moveDeltaConfirmVisible = true;
2147
+ this.MoveDelta = this.buildMoveDelta(recordIds);
2148
+ this.ShowMoveCopyDialog = false;
2149
+ this.MoveDeltaConfirmVisible = true;
1594
2150
  this.cdr.detectChanges();
1595
2151
  }
1596
- onMoveConfirmCancel() {
1597
- this.moveDeltaConfirmVisible = false;
1598
- this.moveDelta = null;
2152
+ /** @deprecated Use {@link ConfirmMoveCopy}. */
2153
+ async confirmMoveCopy() {
2154
+ return this.ConfirmMoveCopy();
2155
+ }
2156
+ OnMoveConfirmCancel() {
2157
+ this.MoveDeltaConfirmVisible = false;
2158
+ this.MoveDelta = null;
1599
2159
  // Re-open the target picker so the user can change their mind without
1600
2160
  // losing their selection.
1601
- this.showMoveCopyDialog = true;
2161
+ this.ShowMoveCopyDialog = true;
1602
2162
  this.cdr.detectChanges();
1603
2163
  }
1604
- async onMoveConfirmCommit(_deltaToken) {
1605
- this.moveDeltaConfirmVisible = false;
2164
+ /** @deprecated Use {@link OnMoveConfirmCancel}. */
2165
+ onMoveConfirmCancel() {
2166
+ return this.OnMoveConfirmCancel();
2167
+ }
2168
+ async OnMoveConfirmCommit(_deltaToken) {
2169
+ this.MoveDeltaConfirmVisible = false;
1606
2170
  this.cdr.detectChanges();
1607
2171
  await this.applyMoveCopy(true);
1608
2172
  }
2173
+ /** @deprecated Use {@link OnMoveConfirmCommit}. */
2174
+ async onMoveConfirmCommit(_deltaToken) {
2175
+ return this.OnMoveConfirmCommit(_deltaToken);
2176
+ }
1609
2177
  /**
1610
2178
  * Pull RecordIDs out of selectedKeys. selectedKeys arrive from the
1611
2179
  * grid in concatenated-key format ("ID|<value>"). For single-PK entities
@@ -1615,8 +2183,8 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1615
2183
  */
1616
2184
  extractSelectedRecordIds() {
1617
2185
  const md = this.ProviderToUse;
1618
- const entityInfo = this.listRecord ? md.EntityByID(this.listRecord.EntityID) : null;
1619
- return this.selectedKeys.map((key) => {
2186
+ const entityInfo = this.ListRecord ? md.EntityByID(this.ListRecord.EntityID) : null;
2187
+ return this.SelectedKeys.map((key) => {
1620
2188
  if (entityInfo && entityInfo.PrimaryKeys.length === 1) {
1621
2189
  const ck = new CompositeKey();
1622
2190
  ck.LoadFromConcatenatedString(key);
@@ -1635,8 +2203,8 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1635
2203
  buildMoveDelta(recordIds) {
1636
2204
  const removeCount = recordIds.length;
1637
2205
  return {
1638
- TargetListId: this.listRecord.ID,
1639
- EntityName: this.listRecord.Entity ?? '',
2206
+ TargetListId: this.ListRecord.ID,
2207
+ EntityName: this.ListRecord.Entity ?? '',
1640
2208
  ToAdd: [],
1641
2209
  ToRemove: recordIds,
1642
2210
  Unchanged: [],
@@ -1645,11 +2213,11 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1645
2213
  Remove: removeCount,
1646
2214
  Unchanged: 0,
1647
2215
  SourceTotal: 0,
1648
- TargetTotal: this.rowCount,
2216
+ TargetTotal: this.RowCount,
1649
2217
  },
1650
2218
  Warnings: [{
1651
2219
  Code: 'WILL_REMOVE_RECORDS',
1652
- Message: `${removeCount} record(s) will be removed from "${this.listRecord.Name}" as part of the move`,
2220
+ Message: `${removeCount} record(s) will be removed from "${this.ListRecord.Name}" as part of the move`,
1653
2221
  Details: { Count: removeCount },
1654
2222
  }],
1655
2223
  // Sentinel — client-side flow doesn't round-trip this.
@@ -1663,16 +2231,16 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1663
2231
  * leave records duplicated in both lists.
1664
2232
  */
1665
2233
  async applyMoveCopy(isMove) {
1666
- if (!this.listRecord || !this.moveCopySelectedTarget)
2234
+ if (!this.ListRecord || !this.MoveCopySelectedTarget)
1667
2235
  return;
1668
- const source = this.listRecord;
1669
- const target = this.moveCopySelectedTarget;
2236
+ const source = this.ListRecord;
2237
+ const target = this.MoveCopySelectedTarget;
1670
2238
  const recordIds = this.extractSelectedRecordIds();
1671
2239
  if (recordIds.length === 0)
1672
2240
  return;
1673
- this.isApplyingMoveCopy = true;
1674
- this.moveCopyTotal = recordIds.length;
1675
- this.moveCopyProgress = 0;
2241
+ this.IsApplyingMoveCopy = true;
2242
+ this.MoveCopyTotal = recordIds.length;
2243
+ this.MoveCopyProgress = 0;
1676
2244
  this.cdr.detectChanges();
1677
2245
  const md = this.ProviderToUse;
1678
2246
  const rv = RunView.FromMetadataProvider(md);
@@ -1720,7 +2288,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1720
2288
  }
1721
2289
  }
1722
2290
  const ok = await tg.Submit();
1723
- this.moveCopyProgress = this.moveCopyTotal;
2291
+ this.MoveCopyProgress = this.MoveCopyTotal;
1724
2292
  if (!ok) {
1725
2293
  this.sharedService.CreateSimpleNotification(`${isMove ? 'Move' : 'Copy'} partially failed — some changes may not have applied`, 'error', 5000);
1726
2294
  return;
@@ -1733,11 +2301,11 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1733
2301
  // refresh by a microtask so the dialog/visibility flips happen in
1734
2302
  // a fresh change-detection cycle.
1735
2303
  await Promise.resolve();
1736
- this.closeMoveCopyDialog();
1737
- this.moveDelta = null;
2304
+ this.CloseMoveCopyDialog();
2305
+ this.MoveDelta = null;
1738
2306
  if (isMove) {
1739
- this.listDetailGrid?.clearSelection();
1740
- this.refreshGrid();
2307
+ this.ListDetailGrid?.clearSelection();
2308
+ this.RefreshGrid();
1741
2309
  this.bumpStatsRefresh();
1742
2310
  }
1743
2311
  this.cdr.detectChanges();
@@ -1748,40 +2316,48 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1748
2316
  this.sharedService.CreateSimpleNotification(`${isMove ? 'Move' : 'Copy'} failed: ${message}`, 'error', 5000);
1749
2317
  }
1750
2318
  finally {
1751
- this.isApplyingMoveCopy = false;
2319
+ this.IsApplyingMoveCopy = false;
1752
2320
  this.cdr.detectChanges();
1753
2321
  }
1754
2322
  }
1755
2323
  // ==========================================
1756
2324
  // Remove from List Dialog
1757
2325
  // ==========================================
1758
- openRemoveDialog() {
1759
- if (this.selectedKeys.length === 0) {
2326
+ OpenRemoveDialog() {
2327
+ if (this.SelectedKeys.length === 0) {
1760
2328
  this.sharedService.CreateSimpleNotification("Please select records to remove", 'warning', 2500);
1761
2329
  return;
1762
2330
  }
1763
- this.showRemoveDialog = true;
2331
+ this.ShowRemoveDialog = true;
1764
2332
  }
2333
+ /** @deprecated Use {@link OpenRemoveDialog}. */
2334
+ openRemoveDialog() {
2335
+ return this.OpenRemoveDialog();
2336
+ }
2337
+ CloseRemoveDialog() {
2338
+ this.ShowRemoveDialog = false;
2339
+ this.IsRemoving = false;
2340
+ this.RemoveProgress = 0;
2341
+ this.RemoveTotal = 0;
2342
+ }
2343
+ /** @deprecated Use {@link CloseRemoveDialog}. */
1765
2344
  closeRemoveDialog() {
1766
- this.showRemoveDialog = false;
1767
- this.isRemoving = false;
1768
- this.removeProgress = 0;
1769
- this.removeTotal = 0;
2345
+ return this.CloseRemoveDialog();
1770
2346
  }
1771
- async confirmRemoveFromList() {
1772
- if (!this.listRecord || this.selectedKeys.length === 0)
2347
+ async ConfirmRemoveFromList() {
2348
+ if (!this.ListRecord || this.SelectedKeys.length === 0)
1773
2349
  return;
1774
- this.isRemoving = true;
1775
- this.removeTotal = this.selectedKeys.length;
1776
- this.removeProgress = 0;
2350
+ this.IsRemoving = true;
2351
+ this.RemoveTotal = this.SelectedKeys.length;
2352
+ this.RemoveProgress = 0;
1777
2353
  const md = this.ProviderToUse;
1778
2354
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
1779
- const entityInfo = md.EntityByID(this.listRecord.EntityID);
2355
+ const entityInfo = md.EntityByID(this.ListRecord.EntityID);
1780
2356
  // selectedKeys from grid are in concatenated format (ID|value)
1781
2357
  // For single PK entities, RecordID in DB is just the raw value
1782
2358
  // For composite PK entities, RecordID in DB is the concatenated format
1783
2359
  // Extract the appropriate format for the query
1784
- const selectedRecordIds = this.selectedKeys.map(key => {
2360
+ const selectedRecordIds = this.SelectedKeys.map(key => {
1785
2361
  if (entityInfo && entityInfo.PrimaryKeys.length === 1) {
1786
2362
  // Single PK: extract just the value from concatenated format
1787
2363
  const compositeKey = new CompositeKey();
@@ -1793,7 +2369,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1793
2369
  return key;
1794
2370
  }
1795
2371
  });
1796
- const listDetailsFilter = `ListID = '${this.listRecord.ID}' AND RecordID IN (${selectedRecordIds.map(id => `'${id}'`).join(',')})`;
2372
+ const listDetailsFilter = `ListID = '${this.ListRecord.ID}' AND RecordID IN (${selectedRecordIds.map(id => `'${id}'`).join(',')})`;
1797
2373
  const listDetailsResult = await rv.RunView({
1798
2374
  EntityName: 'MJ: List Details',
1799
2375
  ExtraFilter: listDetailsFilter,
@@ -1802,7 +2378,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1802
2378
  if (!listDetailsResult.Success) {
1803
2379
  LogError("Error loading list details for removal", undefined, listDetailsResult.ErrorMessage);
1804
2380
  this.sharedService.CreateSimpleNotification("Failed to remove records", 'error', 2500);
1805
- this.isRemoving = false;
2381
+ this.IsRemoving = false;
1806
2382
  return;
1807
2383
  }
1808
2384
  // Use transaction group for bulk delete
@@ -1814,7 +2390,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1814
2390
  }
1815
2391
  const success = await tg.Submit();
1816
2392
  if (success) {
1817
- this.removeProgress = this.removeTotal;
2393
+ this.RemoveProgress = this.RemoveTotal;
1818
2394
  this.sharedService.CreateSimpleNotification(`Removed ${listDetails.length} record${listDetails.length !== 1 ? 's' : ''} from list`, 'success', 2500);
1819
2395
  // Defer cleanup + refresh to the next microtask. Running these
1820
2396
  // synchronously inside the current change-detection cycle
@@ -1823,79 +2399,99 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1823
2399
  // stale state — the deletion succeeds server-side but the UI
1824
2400
  // doesn't re-fetch until the user navigates away and back.
1825
2401
  await Promise.resolve();
1826
- this.closeRemoveDialog();
1827
- this.listDetailGrid?.clearSelection();
1828
- this.refreshGrid();
2402
+ this.CloseRemoveDialog();
2403
+ this.ListDetailGrid?.clearSelection();
2404
+ this.RefreshGrid();
1829
2405
  this.bumpStatsRefresh();
1830
2406
  this.cdr.detectChanges();
1831
2407
  }
1832
2408
  else {
1833
2409
  LogError("Error removing records from list");
1834
2410
  this.sharedService.CreateSimpleNotification("Failed to remove some records", 'error', 2500);
1835
- this.isRemoving = false;
2411
+ this.IsRemoving = false;
1836
2412
  }
1837
2413
  }
2414
+ /** @deprecated Use {@link ConfirmRemoveFromList}. */
2415
+ async confirmRemoveFromList() {
2416
+ return this.ConfirmRemoveFromList();
2417
+ }
1838
2418
  // ==========================================
1839
2419
  // Add Records Dialog
1840
2420
  // ==========================================
1841
- async openAddRecordsDialog() {
1842
- this.showAddRecordsDialog = true;
1843
- this.addableRecords = [];
1844
- this.addRecordsSearchFilter = "";
1845
- this.addDialogLoading = true;
1846
- this.addDialogSaving = false;
2421
+ async OpenAddRecordsDialog() {
2422
+ this.ShowAddRecordsDialog = true;
2423
+ this.AddableRecords = [];
2424
+ this.AddRecordsSearchFilter = "";
2425
+ this.AddDialogLoading = true;
2426
+ this.AddDialogSaving = false;
1847
2427
  // Load existing list detail IDs to mark which records are already in the list
1848
2428
  await this.loadExistingListDetailIds();
1849
- this.addDialogLoading = false;
2429
+ this.AddDialogLoading = false;
1850
2430
  // Explicit CD: the GraphQL promise resolution doesn't reliably produce an
1851
2431
  // Angular tick, so without this the spinner stays up until the next user
1852
2432
  // event (click/keystroke) forces a change-detection cycle.
1853
2433
  this.cdr.detectChanges();
1854
2434
  }
2435
+ /** @deprecated Use {@link OpenAddRecordsDialog}. */
2436
+ async openAddRecordsDialog() {
2437
+ return this.OpenAddRecordsDialog();
2438
+ }
2439
+ CloseAddRecordsDialog() {
2440
+ this.ShowAddRecordsDialog = false;
2441
+ this.AddableRecords = [];
2442
+ this.AddRecordsSearchFilter = "";
2443
+ this.ExistingListDetailIds.clear();
2444
+ this.AddDialogSaving = false;
2445
+ this.AddProgress = 0;
2446
+ this.AddTotal = 0;
2447
+ }
2448
+ /** @deprecated Use {@link CloseAddRecordsDialog}. */
1855
2449
  closeAddRecordsDialog() {
1856
- this.showAddRecordsDialog = false;
1857
- this.addableRecords = [];
1858
- this.addRecordsSearchFilter = "";
1859
- this.existingListDetailIds.clear();
1860
- this.addDialogSaving = false;
1861
- this.addProgress = 0;
1862
- this.addTotal = 0;
2450
+ return this.CloseAddRecordsDialog();
1863
2451
  }
1864
2452
  async loadExistingListDetailIds() {
1865
- if (!this.listRecord)
2453
+ if (!this.ListRecord)
1866
2454
  return;
1867
2455
  const md = this.ProviderToUse;
1868
2456
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
1869
2457
  const result = await rv.RunView({
1870
2458
  EntityName: 'MJ: List Details',
1871
- ExtraFilter: `ListID = '${this.listRecord.ID}'`,
2459
+ ExtraFilter: `ListID = '${this.ListRecord.ID}'`,
1872
2460
  Fields: ['RecordID'],
1873
2461
  ResultType: 'simple'
1874
2462
  }, md.CurrentUser);
1875
2463
  if (result.Success) {
1876
- this.existingListDetailIds = new Set(result.Results.map(r => NormalizeUUID(r.RecordID)));
2464
+ this.ExistingListDetailIds = new Set(result.Results.map(r => NormalizeUUID(r.RecordID)));
1877
2465
  }
1878
2466
  }
1879
- onAddRecordsSearchInputEvent(event) {
2467
+ OnAddRecordsSearchInputEvent(event) {
1880
2468
  const value = event.target.value;
1881
- this.onAddRecordsSearchChange(value);
2469
+ this.OnAddRecordsSearchChange(value);
1882
2470
  }
1883
- onAddRecordsSearchChange(value) {
1884
- this.addRecordsSearchFilter = value;
2471
+ /** @deprecated Use {@link OnAddRecordsSearchInputEvent}. */
2472
+ onAddRecordsSearchInputEvent(event) {
2473
+ return this.OnAddRecordsSearchInputEvent(event);
2474
+ }
2475
+ OnAddRecordsSearchChange(value) {
2476
+ this.AddRecordsSearchFilter = value;
1885
2477
  this.searchSubject.next(value);
1886
2478
  }
2479
+ /** @deprecated Use {@link OnAddRecordsSearchChange}. */
2480
+ onAddRecordsSearchChange(value) {
2481
+ return this.OnAddRecordsSearchChange(value);
2482
+ }
1887
2483
  async searchRecords(searchText) {
1888
- if (!this.listRecord || !searchText || searchText.length < 2) {
1889
- this.addableRecords = [];
2484
+ if (!this.ListRecord || !searchText || searchText.length < 2) {
2485
+ this.AddableRecords = [];
1890
2486
  this.cdr.detectChanges();
1891
2487
  return;
1892
2488
  }
1893
- this.addDialogLoading = true;
2489
+ this.AddDialogLoading = true;
1894
2490
  this.cdr.detectChanges();
1895
2491
  const md = this.ProviderToUse;
1896
- const sourceEntityInfo = md.EntityByID(this.listRecord.EntityID);
2492
+ const sourceEntityInfo = md.EntityByID(this.ListRecord.EntityID);
1897
2493
  if (!sourceEntityInfo) {
1898
- this.addDialogLoading = false;
2494
+ this.AddDialogLoading = false;
1899
2495
  this.cdr.detectChanges();
1900
2496
  return;
1901
2497
  }
@@ -1909,13 +2505,13 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1909
2505
  }
1910
2506
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
1911
2507
  const result = await rv.RunView({
1912
- EntityName: this.listRecord.Entity,
2508
+ EntityName: this.ListRecord.Entity,
1913
2509
  ExtraFilter: filter,
1914
2510
  MaxRows: 100,
1915
2511
  ResultType: 'simple'
1916
2512
  });
1917
2513
  if (result.Success) {
1918
- this.addableRecords = result.Results.map((record) => {
2514
+ this.AddableRecords = result.Results.map((record) => {
1919
2515
  // Compact key segment (raw value, or "F1|v1||F2|v2" for a composite key) — the form ListDetail.RecordID stores
1920
2516
  const recordId = CompositeKey.FromEntityRecord(sourceEntityInfo, record).ToCompactURLSegment();
1921
2517
  return {
@@ -1923,39 +2519,55 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1923
2519
  Name: displayField.Field
1924
2520
  ? FormatRecordDisplayValue(recordId, record[displayField.Field.Name], displayField)
1925
2521
  : recordId,
1926
- isInList: this.existingListDetailIds.has(NormalizeUUID(recordId)),
2522
+ isInList: this.ExistingListDetailIds.has(NormalizeUUID(recordId)),
1927
2523
  isSelected: false
1928
2524
  };
1929
2525
  });
1930
2526
  }
1931
- this.addDialogLoading = false;
2527
+ this.AddDialogLoading = false;
1932
2528
  this.cdr.detectChanges();
1933
2529
  }
1934
- toggleRecordSelection(record) {
2530
+ ToggleRecordSelection(record) {
1935
2531
  if (record.isInList)
1936
2532
  return; // Can't select records already in list
1937
2533
  record.isSelected = !record.isSelected;
1938
2534
  }
2535
+ /** @deprecated Use {@link ToggleRecordSelection}. */
2536
+ toggleRecordSelection(record) {
2537
+ return this.ToggleRecordSelection(record);
2538
+ }
2539
+ get SelectedAddableRecords() {
2540
+ return this.AddableRecords.filter(r => r.isSelected);
2541
+ }
2542
+ /** @deprecated Use {@link SelectedAddableRecords}. */
1939
2543
  get selectedAddableRecords() {
1940
- return this.addableRecords.filter(r => r.isSelected);
2544
+ return this.SelectedAddableRecords;
1941
2545
  }
1942
- selectAllAddable() {
1943
- this.addableRecords.forEach(r => {
2546
+ SelectAllAddable() {
2547
+ this.AddableRecords.forEach(r => {
1944
2548
  if (!r.isInList)
1945
2549
  r.isSelected = true;
1946
2550
  });
1947
2551
  }
2552
+ /** @deprecated Use {@link SelectAllAddable}. */
2553
+ selectAllAddable() {
2554
+ return this.SelectAllAddable();
2555
+ }
2556
+ DeselectAllAddable() {
2557
+ this.AddableRecords.forEach(r => r.isSelected = false);
2558
+ }
2559
+ /** @deprecated Use {@link DeselectAllAddable}. */
1948
2560
  deselectAllAddable() {
1949
- this.addableRecords.forEach(r => r.isSelected = false);
2561
+ return this.DeselectAllAddable();
1950
2562
  }
1951
- async confirmAddRecords() {
1952
- const recordsToAdd = this.selectedAddableRecords;
1953
- if (recordsToAdd.length === 0 || !this.listRecord)
2563
+ async ConfirmAddRecords() {
2564
+ const recordsToAdd = this.SelectedAddableRecords;
2565
+ if (recordsToAdd.length === 0 || !this.ListRecord)
1954
2566
  return;
1955
- this.addDialogSaving = true;
2567
+ this.AddDialogSaving = true;
1956
2568
  // Reserve 20% of progress for tg.Submit()
1957
- this.addTotal = recordsToAdd.length;
1958
- this.addProgress = 0;
2569
+ this.AddTotal = recordsToAdd.length;
2570
+ this.AddProgress = 0;
1959
2571
  const progressPerRecord = 0.8 / recordsToAdd.length; // 80% for individual saves
1960
2572
  const md = this.ProviderToUse;
1961
2573
  // Use transaction group for bulk insert
@@ -1963,7 +2575,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1963
2575
  for (let i = 0; i < recordsToAdd.length; i++) {
1964
2576
  const record = recordsToAdd[i];
1965
2577
  const listDetail = await md.GetEntityObject("MJ: List Details", md.CurrentUser);
1966
- listDetail.ListID = this.listRecord.ID;
2578
+ listDetail.ListID = this.ListRecord.ID;
1967
2579
  listDetail.RecordID = record.ID;
1968
2580
  listDetail.TransactionGroup = tg;
1969
2581
  const result = await listDetail.Save();
@@ -1973,86 +2585,106 @@ export class SingleListDetailComponent extends BaseAngularComponent {
1973
2585
  });
1974
2586
  }
1975
2587
  // Update progress (0-80%)
1976
- this.addProgress = Math.round((i + 1) * progressPerRecord * this.addTotal);
2588
+ this.AddProgress = Math.round((i + 1) * progressPerRecord * this.AddTotal);
1977
2589
  }
1978
2590
  // Show 80% complete before submit
1979
- this.addProgress = Math.round(this.addTotal * 0.8);
2591
+ this.AddProgress = Math.round(this.AddTotal * 0.8);
1980
2592
  const success = await tg.Submit();
1981
2593
  if (success) {
1982
- this.addProgress = this.addTotal;
2594
+ this.AddProgress = this.AddTotal;
1983
2595
  this.sharedService.CreateSimpleNotification(`Added ${recordsToAdd.length} record${recordsToAdd.length !== 1 ? 's' : ''} to list`, 'success', 2500);
1984
2596
  // Defer dialog close + grid refresh to the next microtask. See
1985
2597
  // confirmRemoveFromList for the NG0100 background.
1986
2598
  await Promise.resolve();
1987
- this.closeAddRecordsDialog();
1988
- this.refreshGrid();
2599
+ this.CloseAddRecordsDialog();
2600
+ this.RefreshGrid();
1989
2601
  this.bumpStatsRefresh();
1990
2602
  this.cdr.detectChanges();
1991
2603
  }
1992
2604
  else {
1993
2605
  LogError("Error adding records to list");
1994
2606
  this.sharedService.CreateSimpleNotification("Failed to add some records", 'error', 2500);
1995
- this.addDialogSaving = false;
2607
+ this.AddDialogSaving = false;
1996
2608
  }
1997
2609
  }
2610
+ /** @deprecated Use {@link ConfirmAddRecords}. */
2611
+ async confirmAddRecords() {
2612
+ return this.ConfirmAddRecords();
2613
+ }
1998
2614
  // ==========================================
1999
2615
  // Add From View Dialog (existing functionality, cleaned up)
2000
2616
  // ==========================================
2001
- async openAddFromViewDialog() {
2002
- this.showAddFromViewDialog = true;
2003
- this.userViewsToAdd = [];
2617
+ async OpenAddFromViewDialog() {
2618
+ this.ShowAddFromViewDialog = true;
2619
+ this.UserViewsToAdd = [];
2004
2620
  this.userViewsToAddIds.clear();
2005
- if (!this.userViews) {
2621
+ if (!this.UserViews) {
2006
2622
  await this.loadEntityViews();
2007
2623
  }
2008
2624
  }
2009
- closeAddFromViewDialog() {
2010
- this.showAddFromViewDialog = false;
2011
- this.userViewsToAdd = [];
2625
+ /** @deprecated Use {@link OpenAddFromViewDialog}. */
2626
+ async openAddFromViewDialog() {
2627
+ return this.OpenAddFromViewDialog();
2628
+ }
2629
+ CloseAddFromViewDialog() {
2630
+ this.ShowAddFromViewDialog = false;
2631
+ this.UserViewsToAdd = [];
2012
2632
  this.userViewsToAddIds.clear();
2013
- this.showAddFromViewLoader = false;
2014
- this.addFromViewTotal = 0;
2633
+ this.ShowAddFromViewLoader = false;
2634
+ this.AddFromViewTotal = 0;
2015
2635
  this.setAddFromViewProgress(0);
2016
2636
  }
2637
+ /** @deprecated Use {@link CloseAddFromViewDialog}. */
2638
+ closeAddFromViewDialog() {
2639
+ return this.CloseAddFromViewDialog();
2640
+ }
2017
2641
  async loadEntityViews() {
2018
- if (!this.listRecord || !this.listRecord.Entity)
2642
+ if (!this.ListRecord || !this.ListRecord.Entity)
2019
2643
  return;
2020
- this.showAddFromViewLoader = true;
2644
+ this.ShowAddFromViewLoader = true;
2021
2645
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
2022
2646
  const md = this.ProviderToUse;
2023
2647
  const runViewResult = await rv.RunView({
2024
2648
  EntityName: "MJ: User Views",
2025
- ExtraFilter: `UserID = '${md.CurrentUser.ID}' AND EntityID = '${this.listRecord.EntityID}'`,
2649
+ ExtraFilter: `UserID = '${md.CurrentUser.ID}' AND EntityID = '${this.ListRecord.EntityID}'`,
2026
2650
  ResultType: 'entity_object'
2027
2651
  }, md.CurrentUser);
2028
2652
  if (!runViewResult.Success) {
2029
- LogError(`Error loading User Views for entity ${this.listRecord.Entity}`);
2653
+ LogError(`Error loading User Views for entity ${this.ListRecord.Entity}`);
2030
2654
  }
2031
2655
  else {
2032
- this.userViews = runViewResult.Results;
2656
+ this.UserViews = runViewResult.Results;
2033
2657
  }
2034
- this.showAddFromViewLoader = false;
2658
+ this.ShowAddFromViewLoader = false;
2035
2659
  this.cdr.detectChanges();
2036
2660
  }
2037
- toggleViewSelection(view) {
2038
- const index = this.userViewsToAdd.findIndex(v => UUIDsEqual(v.ID, view.ID));
2661
+ ToggleViewSelection(view) {
2662
+ const index = this.UserViewsToAdd.findIndex(v => UUIDsEqual(v.ID, view.ID));
2039
2663
  if (index >= 0) {
2040
- this.userViewsToAdd.splice(index, 1);
2664
+ this.UserViewsToAdd.splice(index, 1);
2041
2665
  this.userViewsToAddIds.delete(NormalizeUUID(view.ID));
2042
2666
  }
2043
2667
  else {
2044
- this.userViewsToAdd.push(view);
2668
+ this.UserViewsToAdd.push(view);
2045
2669
  this.userViewsToAddIds.add(NormalizeUUID(view.ID));
2046
2670
  }
2047
2671
  }
2048
- isViewSelected(view) {
2672
+ /** @deprecated Use {@link ToggleViewSelection}. */
2673
+ toggleViewSelection(view) {
2674
+ return this.ToggleViewSelection(view);
2675
+ }
2676
+ IsViewSelected(view) {
2049
2677
  return this.userViewsToAddIds.has(NormalizeUUID(view.ID));
2050
2678
  }
2051
- async confirmAddFromView() {
2052
- if (!this.listRecord || this.userViewsToAdd.length === 0)
2679
+ /** @deprecated Use {@link IsViewSelected}. */
2680
+ isViewSelected(view) {
2681
+ return this.IsViewSelected(view);
2682
+ }
2683
+ async ConfirmAddFromView() {
2684
+ if (!this.ListRecord || this.UserViewsToAdd.length === 0)
2053
2685
  return;
2054
- this.showAddFromViewLoader = true;
2055
- this.fetchingRecordsToSave = true;
2686
+ this.ShowAddFromViewLoader = true;
2687
+ this.FetchingRecordsToSave = true;
2056
2688
  this.cdr.detectChanges();
2057
2689
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
2058
2690
  const md = this.ProviderToUse;
@@ -2061,7 +2693,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
2061
2693
  // sequential RunView per view (N round-trips → 1). Each view has a distinct ViewID,
2062
2694
  // so this can't collapse to one query, but RunViews parallelizes the round trips.
2063
2695
  const recordIdSet = new Set();
2064
- const viewResults = await rv.RunViews(this.userViewsToAdd.map(userView => ({
2696
+ const viewResults = await rv.RunViews(this.UserViewsToAdd.map(userView => ({
2065
2697
  ViewID: userView.ID,
2066
2698
  ViewEntity: userView,
2067
2699
  Fields: ["ID"]
@@ -2074,15 +2706,15 @@ export class SingleListDetailComponent extends BaseAngularComponent {
2074
2706
  }
2075
2707
  // Filter out records already in the list
2076
2708
  await this.loadExistingListDetailIds();
2077
- const recordsToAdd = [...recordIdSet].filter(id => !this.existingListDetailIds.has(id));
2078
- this.addFromViewTotal = recordsToAdd.length;
2709
+ const recordsToAdd = [...recordIdSet].filter(id => !this.ExistingListDetailIds.has(id));
2710
+ this.AddFromViewTotal = recordsToAdd.length;
2079
2711
  this.setAddFromViewProgress(0);
2080
- this.fetchingRecordsToSave = false;
2712
+ this.FetchingRecordsToSave = false;
2081
2713
  this.cdr.detectChanges();
2082
2714
  const progressPerRecord = 0.8 / Math.max(recordsToAdd.length, 1); // 80% for individual saves
2083
2715
  if (recordsToAdd.length === 0) {
2084
2716
  this.sharedService.CreateSimpleNotification("All records already in list", 'info', 2500);
2085
- this.showAddFromViewLoader = false;
2717
+ this.ShowAddFromViewLoader = false;
2086
2718
  this.cdr.detectChanges();
2087
2719
  return;
2088
2720
  }
@@ -2092,7 +2724,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
2092
2724
  for (let i = 0; i < recordsToAdd.length; i++) {
2093
2725
  const recordID = recordsToAdd[i];
2094
2726
  const listDetail = await md.GetEntityObject("MJ: List Details", md.CurrentUser);
2095
- listDetail.ListID = this.listRecord.ID;
2727
+ listDetail.ListID = this.ListRecord.ID;
2096
2728
  listDetail.RecordID = recordID;
2097
2729
  listDetail.TransactionGroup = tg;
2098
2730
  const result = await listDetail.Save();
@@ -2106,36 +2738,40 @@ export class SingleListDetailComponent extends BaseAngularComponent {
2106
2738
  // CD cycle on this iteration. Without detectChanges() the next await
2107
2739
  // can yield in the middle of a cycle and Angular's dev-mode re-check
2108
2740
  // catches the percent changing → NG0100 flood.
2109
- this.setAddFromViewProgress(Math.round((i + 1) * progressPerRecord * this.addFromViewTotal));
2741
+ this.setAddFromViewProgress(Math.round((i + 1) * progressPerRecord * this.AddFromViewTotal));
2110
2742
  this.cdr.detectChanges();
2111
2743
  }
2112
2744
  // Show 80% complete before submit
2113
- this.setAddFromViewProgress(Math.round(this.addFromViewTotal * 0.8));
2745
+ this.setAddFromViewProgress(Math.round(this.AddFromViewTotal * 0.8));
2114
2746
  this.cdr.detectChanges();
2115
2747
  const success = await tg.Submit();
2116
2748
  if (success) {
2117
- this.setAddFromViewProgress(this.addFromViewTotal);
2749
+ this.setAddFromViewProgress(this.AddFromViewTotal);
2118
2750
  this.sharedService.CreateSimpleNotification(`Added ${recordsToAdd.length} record${recordsToAdd.length !== 1 ? 's' : ''} to list`, 'success', 2500);
2119
2751
  // Defer dialog close + grid refresh to the next microtask. See
2120
2752
  // confirmRemoveFromList for the NG0100 background.
2121
2753
  await Promise.resolve();
2122
- this.closeAddFromViewDialog();
2123
- this.refreshGrid();
2754
+ this.CloseAddFromViewDialog();
2755
+ this.RefreshGrid();
2124
2756
  this.bumpStatsRefresh();
2125
2757
  this.cdr.detectChanges();
2126
2758
  }
2127
2759
  else {
2128
2760
  LogError("Error adding records from view to list");
2129
2761
  this.sharedService.CreateSimpleNotification("Failed to add some records", 'error', 2500);
2130
- this.showAddFromViewLoader = false;
2762
+ this.ShowAddFromViewLoader = false;
2131
2763
  }
2132
2764
  }
2765
+ /** @deprecated Use {@link ConfirmAddFromView}. */
2766
+ async confirmAddFromView() {
2767
+ return this.ConfirmAddFromView();
2768
+ }
2133
2769
  static ɵfac = function SingleListDetailComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SingleListDetailComponent)(i0.ɵɵdirectiveInject(i1.SharedService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i2.ExportService), i0.ɵɵdirectiveInject(i3.ListSharingService)); };
2134
2770
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SingleListDetailComponent, selectors: [["mj-list-detail"]], viewQuery: function SingleListDetailComponent_Query(rf, ctx) { if (rf & 1) {
2135
2771
  i0.ɵɵviewQuery(_c0, 5);
2136
2772
  } if (rf & 2) {
2137
2773
  let _t;
2138
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.listDetailGrid = _t.first);
2774
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.ListDetailGrid = _t.first);
2139
2775
  } }, hostBindings: function SingleListDetailComponent_HostBindings(rf, ctx) { if (rf & 1) {
2140
2776
  i0.ɵɵlistener("click", function SingleListDetailComponent_click_HostBindingHandler($event) { return ctx.onDocumentClick($event); }, i0.ɵɵresolveDocument);
2141
2777
  } }, inputs: { ListID: "ListID" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 19, vars: 20, consts: [["listDetailGrid", ""], [1, "list-detail-container"], [1, "list-header"], [1, "list-header__titleblock"], [1, "lineage-badge", 3, "title"], [1, "list-detail__tags"], [1, "add-dropdown-wrapper"], [1, "permission-badge", "permission-badge--viewer"], [1, "custom-toolbar"], [1, "loading-container"], [1, "grid-with-stats"], [3, "Confirm", "Cancel", "Visible", "Delta", "TargetListName", "SourceLabel"], [3, "Visible", "Title", "MinWidth", "Width", "Height"], ["Title", "Remove from List", 3, "Visible", "MinWidth", "Width"], ["Title", "Add Records from Views", 3, "Visible", "MinWidth", "Width", "Height"], [1, "fa-solid", "fa-link"], [1, "lineage-badge__refreshed"], ["EntityName", "MJ: Lists", 3, "Provider", "RecordID", "Editable", "MaxDisplay"], ["mjButton", "", "variant", "primary", 1, "add-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "fa-solid", "fa-chevron-down", "add-dropdown-arrow"], [1, "add-dropdown-panel"], [1, "add-dropdown-item"], [1, "add-dropdown-item", 3, "click"], [1, "add-dropdown-item-text"], [1, "add-dropdown-item-desc"], [1, "fa-solid", "fa-eye"], [1, "toolbar-left"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], ["mjButton", "", "variant", "flat", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-rotate"], [1, "fa-solid", "fa-file-export"], ["mjButton", "", "variant", "flat", "title", "Move selected records to another list", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-arrow-right-arrow-left"], ["mjButton", "", "variant", "flat", "title", "Copy selected records to another list (keeps them here too)", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-copy"], ["title", "Set status on selected items", 1, "mj-input", "mj-select", "bulk-status-select", 3, "ngModelChange", "ngModel", "disabled"], ["value", ""], ["value", "Active"], ["value", "Complete"], ["value", "Pending"], ["value", "Disabled"], ["value", "Rejected"], ["mjButton", "", "variant", "flat", 1, "toolbar-button", 3, "click", "disabled"], ["mjButton", "", "variant", "flat", 1, "toolbar-button", "remove-btn", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-pen-to-square"], ["title", "Refresh mode", 1, "mj-input", "mj-select", "refresh-mode-select", 3, "ngModelChange", "ngModel", "disabled"], ["value", "Additive"], ["value", "Sync"], [1, "fa-solid", "fa-cloud-arrow-down"], ["text", "Loading list...", "size", "medium"], [1, "grid-container"], [3, "rowClicked", "rowDoubleClicked", "selectionChange", "dataLoaded", "listId", "listEntity", "autoNavigate", "height", "showToolbar", "toolbarConfig", "selectionMode"], [1, "list-detail__stats"], [3, "Provider", "ListID", "RefreshTrigger"], [3, "Close", "Visible", "Title", "MinWidth", "Width", "Height"], [1, "dialog-content", "move-copy-dialog"], [1, "dialog-loading"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", "variant", "outline", 3, "click", "disabled"], ["size", "small", 3, "text"], [1, "progress-bar", 3, "Value"], [1, "dialog-instruction"], [1, "search-section"], [1, "search-input-wrapper"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", "placeholder", "Filter lists by name\u2026", 1, "mj-input", "search-input", 3, "ngModelChange", "ngModel"], [1, "list-loading"], ["Size", "compact", "Icon", "fa-solid fa-list", "Title", "No other lists found for this entity."], [1, "records-scroll", "move-copy-targets"], ["text", "Loading lists...", "size", "small"], [1, "dialog-note"], [1, "record-item", 3, "selected"], [1, "record-item", 3, "click"], [1, "record-checkbox"], ["type", "radio", "name", "moveCopyTarget", 3, "click", "change", "checked"], [1, "record-name"], [1, "target-desc"], ["Title", "Remove from List", 3, "Close", "Visible", "MinWidth", "Width"], [1, "dialog-content"], [1, "dialog-message"], ["mjButton", "", "variant", "danger", 3, "click", "disabled"], ["text", "Removing records...", "size", "small"], [1, "fa-solid", "fa-triangle-exclamation", "warning-icon"], [1, "dialog-content", "add-records-dialog"], ["text", "Adding records to list...", "size", "small"], ["type", "text", 1, "mj-input", "search-input", 3, "input", "placeholder", "value"], [1, "search-hint"], [1, "records-list"], ["Variant", "no-results", "Size", "compact", "Icon", "fa-solid fa-magnifying-glass", 3, "Title"], ["Size", "compact", "Icon", "fa-solid fa-list", "Title", "Search for records to add to this list"], ["size", "small", 3, "showText"], [1, "selection-controls"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click"], [1, "selection-info"], [1, "records-scroll"], [1, "record-item", 3, "in-list", "selected"], ["title", "Already in list", 1, "fa-solid", "fa-check", "in-list-icon"], ["type", "checkbox", 3, "checked"], [1, "in-list-badge"], ["type", "checkbox", 3, "click", "change", "checked"], [1, "dialog-content", "export-dialog"], [1, "export-section-label"], [1, "export-format-row"], [1, "export-format-option", 3, "click"], ["type", "radio", "name", "exportFormat", "value", "excel", 3, "ngModelChange", "ngModel"], [1, "fa-solid", "fa-file-excel", 2, "color", "var(--mj-status-success)"], ["type", "radio", "name", "exportFormat", "value", "csv", 3, "ngModelChange", "ngModel"], [1, "fa-solid", "fa-file-csv", 2, "color", "var(--mj-text-secondary)"], ["type", "radio", "name", "exportFormat", "value", "json", 3, "ngModelChange", "ngModel"], [1, "fa-solid", "fa-file-code", 2, "color", "var(--mj-brand-primary)"], [1, "export-section-divider"], [1, "export-fields-list"], [1, "export-field-row"], [1, "export-field-actions"], ["href", "#", 3, "click"], [1, "muted"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], [1, "fa-solid", "fa-download"], ["Title", "Add Records from Views", 3, "Close", "Visible", "MinWidth", "Width", "Height"], ["text", "Loading records from views...", "size", "small"], ["text", "Loading views...", "size", "small"], [1, "views-list"], ["Size", "compact", "Icon", "fa-solid fa-folder-open", "Title", "No saved views found for this entity"], [1, "view-item", 3, "selected"], [1, "view-item", 3, "click"], [1, "fa-solid", "fa-table-list", "view-icon"], [1, "view-name"]], template: function SingleListDetailComponent_Template(rf, ctx) { if (rf & 1) {
@@ -2196,7 +2832,7 @@ export class SingleListDetailComponent extends BaseAngularComponent {
2196
2832
  args: [{ standalone: false, selector: 'mj-list-detail', template: "<div class=\"list-detail-container\">\n <!-- Header with title and Add button -->\n <div class=\"list-header\">\n <div class=\"list-header__titleblock\">\n @if (listRecord) {\n <h1>{{ listRecord.Name }}</h1>\n } @else {\n <h1>List</h1>\n }\n @if (hasLineage) {\n <div class=\"lineage-badge\" [title]=\"'Source view: ' + (sourceViewName ?? 'unknown')\">\n <i class=\"fa-solid fa-link\"></i>\n <span>\n @if (listRecord?.UseSnapshot) {\n Snapshot of: <strong>{{ sourceViewName ?? '(unknown view)' }}</strong>\n } @else {\n Live view: <strong>{{ sourceViewName ?? '(unknown view)' }}</strong>\n }\n </span>\n @if (listRecord?.LastRefreshedAt) {\n <span class=\"lineage-badge__refreshed\">\n \u00B7 last refreshed {{ listRecord!.LastRefreshedAt | date:'medium' }}\n </span>\n }\n </div>\n }\n @if (listRecord) {\n <!-- Tags section (Phase 4.2). Editable on the detail view. -->\n <div class=\"list-detail__tags\">\n <mj-tag-chips\n [Provider]=\"Provider\"\n EntityName=\"MJ: Lists\"\n [RecordID]=\"listRecord.ID\"\n [Editable]=\"capabilities.CanEdit\"\n [MaxDisplay]=\"20\">\n </mj-tag-chips>\n </div>\n }\n </div>\n @if (capabilities.CanEdit) {\n <div class=\"add-dropdown-wrapper\">\n <button mjButton variant=\"primary\" class=\"add-btn\" (click)=\"toggleAddDropdown()\">\n <span class=\"fa-solid fa-plus\"></span>\n Add to List\n <span class=\"fa-solid fa-chevron-down add-dropdown-arrow\"></span>\n </button>\n @if (showAddDropdown) {\n <div class=\"add-dropdown-panel\">\n @for (option of addOptions; track option.Text) {\n <div class=\"add-dropdown-item\" (click)=\"onDropdownItemClick(option)\">\n <span class=\"add-dropdown-item-text\">{{ option.Text }}</span>\n @if (option.Description) {\n <span class=\"add-dropdown-item-desc\">{{ option.Description }}</span>\n }\n </div>\n }\n </div>\n }\n </div>\n } @else if (currentLevel) {\n <!-- Viewer-perspective badge so users understand why mutation\n controls are absent. Mirrors mockup 19. -->\n <span class=\"permission-badge permission-badge--viewer\">\n <i class=\"fa-solid fa-eye\"></i>\n Viewer access\n </span>\n }\n </div>\n\n <!-- Custom Toolbar -->\n @if (!showLoader && listRecord) {\n <div class=\"custom-toolbar\">\n <div class=\"toolbar-left\">\n <span class=\"row-count\">{{ rowCount }} row{{ rowCount !== 1 ? 's' : '' }}</span>\n @if (selectedKeys.length > 0) {\n <span class=\"selection-count\">({{ selectedKeys.length }} selected)</span>\n }\n </div>\n <div class=\"toolbar-right\">\n @if (selectedKeys.length > 0 && capabilities.CanEdit) {\n <!-- Bulk-edit toolbar (Phase 5.2, mockups 23/24).\n Move/Copy open the target-list picker. Move then routes\n through the delta-confirm dialog because the source side\n drops; Copy is additive and applies directly. -->\n <button mjButton\n (click)=\"openMoveDialog()\"\n variant=\"flat\"\n class=\"toolbar-button\"\n title=\"Move selected records to another list\">\n <span class=\"fa-solid fa-arrow-right-arrow-left\"></span>\n Move\u2026\n </button>\n <button mjButton\n (click)=\"openCopyDialog()\"\n variant=\"flat\"\n class=\"toolbar-button\"\n title=\"Copy selected records to another list (keeps them here too)\">\n <span class=\"fa-solid fa-copy\"></span>\n Copy\u2026\n </button>\n <select\n class=\"mj-input mj-select bulk-status-select\"\n [(ngModel)]=\"bulkStatus\"\n [disabled]=\"isApplyingBulkStatus\"\n title=\"Set status on selected items\">\n <option value=\"\">Set status\u2026</option>\n <option value=\"Active\">Active</option>\n <option value=\"Complete\">Complete</option>\n <option value=\"Pending\">Pending</option>\n <option value=\"Disabled\">Disabled</option>\n <option value=\"Rejected\">Rejected</option>\n </select>\n <button mjButton\n (click)=\"applyBulkStatus()\"\n [disabled]=\"!bulkStatus || isApplyingBulkStatus\"\n variant=\"flat\"\n class=\"toolbar-button\">\n @if (isApplyingBulkStatus) {\n <span class=\"fa-solid fa-spinner fa-spin\"></span>\n Applying\u2026\n } @else {\n <span class=\"fa-solid fa-pen-to-square\"></span>\n Apply Status\n }\n </button>\n <button mjButton\n (click)=\"openRemoveDialog()\"\n variant=\"flat\"\n class=\"toolbar-button remove-btn\">\n <span class=\"fa-solid fa-trash\"></span>\n Remove from List\n </button>\n }\n @if (hasLineage && capabilities.CanRefresh) {\n <select\n class=\"mj-input mj-select refresh-mode-select\"\n [ngModel]=\"refreshMode\"\n (ngModelChange)=\"onRefreshModeChange($event)\"\n [disabled]=\"isPreviewingRefresh || isApplyingRefresh\"\n title=\"Refresh mode\">\n <option value=\"Additive\">Additive</option>\n <option value=\"Sync\">Sync</option>\n </select>\n <button mjButton\n (click)=\"onRefreshFromSource()\"\n [disabled]=\"isPreviewingRefresh || isApplyingRefresh\"\n variant=\"flat\"\n class=\"toolbar-button\">\n @if (isPreviewingRefresh) {\n <span class=\"fa-solid fa-spinner fa-spin\"></span>\n Previewing...\n } @else {\n <span class=\"fa-solid fa-cloud-arrow-down\"></span>\n Refresh from Source\n }\n </button>\n }\n <button mjButton\n (click)=\"onRefreshClick()\"\n variant=\"flat\"\n class=\"toolbar-button\">\n <span class=\"fa-solid fa-rotate\"></span>\n Refresh\n </button>\n <button mjButton\n (click)=\"onExportClick()\"\n variant=\"flat\"\n class=\"toolbar-button\">\n <span class=\"fa-solid fa-file-export\"></span>\n Export\n </button>\n </div>\n </div>\n }\n\n <!-- Loading State -->\n @if (showLoader) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading list...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Grid + side stats panel (Phase 5.4). -->\n @if (!showLoader && listRecord) {\n <div class=\"grid-with-stats\">\n <div class=\"grid-container\">\n <mj-list-detail-grid\n #listDetailGrid\n [listId]=\"ListID\"\n [listEntity]=\"listRecord\"\n [autoNavigate]=\"true\"\n [height]=\"'auto'\"\n [showToolbar]=\"false\"\n [toolbarConfig]=\"gridToolbarConfig\"\n [selectionMode]=\"'checkbox'\"\n (rowClicked)=\"onRowClicked($event)\"\n (rowDoubleClicked)=\"onRowDoubleClicked($event)\"\n (selectionChange)=\"onSelectionChange($event)\"\n (dataLoaded)=\"onDataLoaded($event)\">\n </mj-list-detail-grid>\n </div>\n <aside class=\"list-detail__stats\">\n <mj-list-stats [Provider]=\"Provider\" [ListID]=\"listRecord.ID\" [RefreshTrigger]=\"statsRefreshTrigger\"></mj-list-stats>\n </aside>\n </div>\n }\n\n <!-- Refresh-from-source delta-confirm dialog -->\n <mj-list-delta-confirm\n [Visible]=\"refreshConfirmVisible && !!refreshDelta\"\n [Delta]=\"refreshDelta\"\n [TargetListName]=\"listRecord?.Name ?? null\"\n [SourceLabel]=\"sourceViewName\"\n (Confirm)=\"onRefreshConfirmCommit($event)\"\n (Cancel)=\"onRefreshConfirmCancel()\">\n </mj-list-delta-confirm>\n\n <!-- Move delta-confirm dialog (mockup 24). Shown only for Move,\n never for Copy. Drops on the source side require the user to\n acknowledge before Confirm enables. -->\n <mj-list-delta-confirm\n [Visible]=\"moveDeltaConfirmVisible && !!moveDelta\"\n [Delta]=\"moveDelta\"\n [TargetListName]=\"listRecord?.Name ?? null\"\n [SourceLabel]=\"'Move to ' + (moveCopySelectedTarget?.Name ?? 'target list')\"\n (Confirm)=\"onMoveConfirmCommit($event)\"\n (Cancel)=\"onMoveConfirmCancel()\">\n </mj-list-delta-confirm>\n</div>\n\n<!-- Move/Copy target picker (mockup 23). Lists every list owned by /\n shared with the current user that targets the same entity. -->\n@if (showMoveCopyDialog) {\n <mj-dialog\n [Visible]=\"true\"\n [Title]=\"(moveCopyMode === 'move' ? 'Move ' : 'Copy ') + selectedKeys.length + ' record' + (selectedKeys.length === 1 ? '' : 's') + ' to\u2026'\"\n (Close)=\"closeMoveCopyDialog()\"\n [MinWidth]=\"480\"\n [Width]=\"640\"\n [Height]=\"560\">\n <div class=\"dialog-content move-copy-dialog\">\n @if (isApplyingMoveCopy) {\n <div class=\"dialog-loading\">\n <mj-loading [text]=\"(moveCopyMode === 'move' ? 'Moving' : 'Copying') + ' records...'\" size=\"small\"></mj-loading>\n @if (moveCopyTotal > 0) {\n <mj-progress-bar\n [Value]=\"moveCopyProgressPercent\"\n class=\"progress-bar\">\n </mj-progress-bar>\n }\n </div>\n } @else {\n <p class=\"dialog-instruction\">\n Choose the target list. Both lists must hold the same entity ({{ listRecord?.Entity ?? 'records' }}).\n </p>\n <div class=\"search-section\">\n <div class=\"search-input-wrapper\">\n <span class=\"fa-solid fa-search search-icon\"></span>\n <input\n class=\"mj-input search-input\"\n type=\"text\"\n placeholder=\"Filter lists by name\u2026\"\n [(ngModel)]=\"moveCopyTargetSearch\" />\n </div>\n </div>\n @if (moveCopyTargetCandidatesLoading) {\n <div class=\"list-loading\">\n <mj-loading text=\"Loading lists...\" size=\"small\"></mj-loading>\n </div>\n } @else if (filteredMoveCopyTargets.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-list\"\n Title=\"No other lists found for this entity.\">\n <p class=\"dialog-note\">Create a new list first if you need a new target.</p>\n </mj-empty-state>\n } @else {\n <div class=\"records-scroll move-copy-targets\">\n @for (target of filteredMoveCopyTargets; track target.ID) {\n <div class=\"record-item\"\n [class.selected]=\"moveCopySelectedTarget?.ID === target.ID\"\n (click)=\"selectMoveCopyTarget(target)\">\n <div class=\"record-checkbox\">\n <input type=\"radio\"\n name=\"moveCopyTarget\"\n [checked]=\"moveCopySelectedTarget?.ID === target.ID\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"selectMoveCopyTarget(target)\">\n </div>\n <div class=\"record-name\">\n <strong>{{ target.Name }}</strong>\n @if (target.Description) {\n <span class=\"target-desc\"> \u2014 {{ target.Description }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n <mj-dialog-actions>\n <button mjButton\n (click)=\"confirmMoveCopy()\"\n [disabled]=\"!moveCopySelectedTarget || isApplyingMoveCopy\"\n variant=\"primary\">\n @if (moveCopyMode === 'move') {\n <span class=\"fa-solid fa-arrow-right-arrow-left\"></span>\n Continue\n } @else {\n <span class=\"fa-solid fa-copy\"></span>\n Copy {{ selectedKeys.length }}\n }\n </button>\n <button mjButton\n (click)=\"closeMoveCopyDialog()\"\n [disabled]=\"isApplyingMoveCopy\"\n variant=\"outline\">\n Cancel\n </button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n\n<!-- Remove from List Confirmation Dialog -->\n@if (showRemoveDialog) {\n <mj-dialog\n [Visible]=\"true\"\n Title=\"Remove from List\"\n (Close)=\"closeRemoveDialog()\"\n [MinWidth]=\"350\"\n [Width]=\"450\">\n <div class=\"dialog-content\">\n @if (isRemoving) {\n <div class=\"dialog-loading\">\n <mj-loading text=\"Removing records...\" size=\"small\"></mj-loading>\n @if (removeTotal > 0) {\n <mj-progress-bar\n [Value]=\"removeProgressPercent\"\n class=\"progress-bar\">\n </mj-progress-bar>\n }\n </div>\n } @else {\n <div class=\"dialog-message\">\n <span class=\"fa-solid fa-triangle-exclamation warning-icon\"></span>\n <p>Are you sure you want to remove <strong>{{ selectedKeys.length }}</strong> record{{ selectedKeys.length !== 1 ? 's' : '' }} from this list?</p>\n <p class=\"dialog-note\">This will not delete the records, only remove them from this list.</p>\n </div>\n }\n </div>\n <mj-dialog-actions>\n <button mjButton\n (click)=\"confirmRemoveFromList()\"\n [disabled]=\"isRemoving\"\n variant=\"danger\">\n Remove\n </button>\n <button mjButton\n (click)=\"closeRemoveDialog()\"\n [disabled]=\"isRemoving\"\n variant=\"outline\">\n Cancel\n </button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n\n<!-- Add Records Dialog -->\n@if (showAddRecordsDialog) {\n <mj-dialog\n [Visible]=\"true\"\n [Title]=\"'Add ' + (listRecord?.Entity || 'Records') + ' to List'\"\n (Close)=\"closeAddRecordsDialog()\"\n [MinWidth]=\"500\"\n [Width]=\"700\"\n [Height]=\"650\">\n <div class=\"dialog-content add-records-dialog\">\n @if (addDialogSaving) {\n <div class=\"dialog-loading\">\n <mj-loading text=\"Adding records to list...\" size=\"small\"></mj-loading>\n @if (addTotal > 0) {\n <mj-progress-bar\n [Value]=\"addProgressPercent\"\n class=\"progress-bar\">\n </mj-progress-bar>\n }\n </div>\n } @else {\n <!-- Search Input -->\n <div class=\"search-section\">\n <div class=\"search-input-wrapper\">\n <span class=\"fa-solid fa-search search-icon\"></span>\n <input\n class=\"mj-input search-input\"\n type=\"text\"\n [placeholder]=\"'Search ' + (listRecord?.Entity || 'records') + '...'\"\n [value]=\"addRecordsSearchFilter\"\n (input)=\"onAddRecordsSearchInputEvent($event)\" />\n </div>\n <span class=\"search-hint\">Type at least 2 characters to search</span>\n </div>\n <!-- Records List -->\n <div class=\"records-list\">\n @if (addDialogLoading) {\n <div class=\"list-loading\">\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n </div>\n } @else if (addableRecords.length === 0 && addRecordsSearchFilter.length >= 2) {\n <mj-empty-state\n Variant=\"no-results\"\n Size=\"compact\"\n Icon=\"fa-solid fa-magnifying-glass\"\n [Title]=\"AddRecordsNoMatchTitle\" />\n } @else if (addableRecords.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-list\"\n Title=\"Search for records to add to this list\" />\n } @else {\n <!-- Selection controls -->\n <div class=\"selection-controls\">\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"selectAllAddable()\">Select All</button>\n <button mjButton variant=\"flat\" size=\"sm\" (click)=\"deselectAllAddable()\">Deselect All</button>\n <span class=\"selection-info\">{{ selectedAddableRecords.length }} selected</span>\n </div>\n <!-- Records -->\n <div class=\"records-scroll\">\n @for (record of addableRecords; track record.ID) {\n <div class=\"record-item\"\n [class.in-list]=\"record.isInList\"\n [class.selected]=\"record.isSelected\"\n (click)=\"toggleRecordSelection(record)\">\n <div class=\"record-checkbox\">\n @if (record.isInList) {\n <span class=\"fa-solid fa-check in-list-icon\" title=\"Already in list\"></span>\n } @else {\n <input type=\"checkbox\"\n [checked]=\"record.isSelected\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRecordSelection(record)\">\n }\n </div>\n <div class=\"record-name\">{{ record.Name }}</div>\n @if (record.isInList) {\n <span class=\"in-list-badge\">In List</span>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <mj-dialog-actions>\n <button mjButton\n (click)=\"confirmAddRecords()\"\n [disabled]=\"addDialogSaving || selectedAddableRecords.length === 0\"\n variant=\"primary\">\n Add {{ selectedAddableRecords.length > 0 ? selectedAddableRecords.length : '' }} Record{{ selectedAddableRecords.length !== 1 ? 's' : '' }}\n </button>\n <button mjButton\n (click)=\"closeAddRecordsDialog()\"\n [disabled]=\"addDialogSaving\"\n variant=\"outline\">\n Cancel\n </button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n\n<!-- Export Dialog (mockup 26) \u2014 format + column picker -->\n@if (showExportDialog && listRecord) {\n <mj-dialog\n [Visible]=\"true\"\n [Title]=\"'Export ' + exportRecordCount + ' Record' + (exportRecordCount === 1 ? '' : 's')\"\n (Close)=\"closeExportDialog()\"\n [MinWidth]=\"520\"\n [Width]=\"640\"\n [Height]=\"640\">\n <div class=\"dialog-content export-dialog\">\n <label class=\"export-section-label\">Format</label>\n <div class=\"export-format-row\">\n <label class=\"export-format-option\"\n [class.export-format-option--selected]=\"exportFormat === 'excel'\"\n (click)=\"exportFormat = 'excel'\">\n <input type=\"radio\" name=\"exportFormat\" value=\"excel\" [(ngModel)]=\"exportFormat\">\n <i class=\"fa-solid fa-file-excel\" style=\"color: var(--mj-status-success);\"></i>\n <span>Excel (.xlsx)</span>\n </label>\n <label class=\"export-format-option\"\n [class.export-format-option--selected]=\"exportFormat === 'csv'\"\n (click)=\"exportFormat = 'csv'\">\n <input type=\"radio\" name=\"exportFormat\" value=\"csv\" [(ngModel)]=\"exportFormat\">\n <i class=\"fa-solid fa-file-csv\" style=\"color: var(--mj-text-secondary);\"></i>\n <span>CSV</span>\n </label>\n <label class=\"export-format-option\"\n [class.export-format-option--selected]=\"exportFormat === 'json'\"\n (click)=\"exportFormat = 'json'\">\n <input type=\"radio\" name=\"exportFormat\" value=\"json\" [(ngModel)]=\"exportFormat\">\n <i class=\"fa-solid fa-file-code\" style=\"color: var(--mj-brand-primary);\"></i>\n <span>JSON</span>\n </label>\n </div>\n\n <div class=\"export-section-divider\"></div>\n\n <label class=\"export-section-label\">Columns to include</label>\n <div class=\"export-fields-list\">\n @for (f of exportFields; track f.Name) {\n <label class=\"export-field-row\">\n <input type=\"checkbox\" [(ngModel)]=\"f.Selected\">\n <span>{{ f.DisplayName }}</span>\n </label>\n }\n </div>\n <div class=\"export-field-actions\">\n <a href=\"#\" (click)=\"selectAllExportFields(); $event.preventDefault()\">Select All</a>\n <a href=\"#\" (click)=\"selectNoneExportFields(); $event.preventDefault()\">Select None</a>\n <span class=\"muted\">{{ selectedExportFieldCount }} of {{ exportFields.length }} selected</span>\n </div>\n </div>\n <mj-dialog-actions>\n <button mjButton variant=\"primary\"\n [disabled]=\"selectedExportFieldCount === 0 || isExporting\"\n (click)=\"executeExport()\">\n @if (isExporting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Exporting\u2026\n } @else {\n <i class=\"fa-solid fa-download\"></i>\n Export {{ exportRecordCount }} Record{{ exportRecordCount === 1 ? '' : 's' }}\n }\n </button>\n <button mjButton variant=\"outline\" [disabled]=\"isExporting\" (click)=\"closeExportDialog()\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n\n<!-- Add From View Dialog -->\n@if (showAddFromViewDialog) {\n <mj-dialog\n [Visible]=\"true\"\n Title=\"Add Records from Views\"\n (Close)=\"closeAddFromViewDialog()\"\n [MinWidth]=\"400\"\n [Width]=\"600\"\n [Height]=\"500\">\n <div class=\"dialog-content\">\n @if (showAddFromViewLoader && fetchingRecordsToSave) {\n <div class=\"dialog-loading\">\n <mj-loading text=\"Loading records from views...\" size=\"small\"></mj-loading>\n </div>\n } @else if (showAddFromViewLoader && addFromViewTotal > 0) {\n <div class=\"dialog-loading\">\n <mj-loading text=\"Adding records to list...\" size=\"small\"></mj-loading>\n <mj-progress-bar\n [Value]=\"addFromViewProgressPercent\"\n class=\"progress-bar\">\n </mj-progress-bar>\n </div>\n } @else if (showAddFromViewLoader) {\n <div class=\"dialog-loading\">\n <mj-loading text=\"Loading views...\" size=\"small\"></mj-loading>\n </div>\n } @else {\n <p class=\"dialog-instruction\">Select views to add their records to this list:</p>\n <div class=\"views-list\">\n @if (!userViews || userViews.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-folder-open\"\n Title=\"No saved views found for this entity\" />\n } @else {\n @for (view of userViews; track view.ID) {\n <div class=\"view-item\"\n [class.selected]=\"isViewSelected(view)\"\n (click)=\"toggleViewSelection(view)\">\n <input type=\"checkbox\"\n [checked]=\"isViewSelected(view)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleViewSelection(view)\">\n <span class=\"fa-solid fa-table-list view-icon\"></span>\n <span class=\"view-name\">{{ view.Name }}</span>\n </div>\n }\n }\n </div>\n }\n </div>\n <mj-dialog-actions>\n <button mjButton\n (click)=\"confirmAddFromView()\"\n [disabled]=\"showAddFromViewLoader || userViewsToAdd.length === 0\"\n variant=\"primary\">\n Add from {{ userViewsToAdd.length }} View{{ userViewsToAdd.length !== 1 ? 's' : '' }}\n </button>\n <button mjButton\n (click)=\"closeAddFromViewDialog()\"\n [disabled]=\"showAddFromViewLoader\"\n variant=\"outline\">\n Cancel\n </button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n", styles: [".list-detail-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n padding: 20px;\n background: var(--mj-bg-surface-card);\n}\n\n.list-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 16px;\n flex-shrink: 0;\n gap: 16px;\n}\n\n.list-header__titleblock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.list-header h1 {\n margin: 0;\n font-size: 24px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.lineage-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, var(--mj-border-default));\n color: var(--mj-text-secondary);\n font-size: 12px;\n width: fit-content;\n}\n\n.lineage-badge i {\n color: var(--mj-brand-primary);\n}\n\n.lineage-badge__refreshed {\n color: var(--mj-text-muted);\n}\n\n.refresh-mode-select {\n min-width: 100px;\n font-size: 13px;\n}\n\n.bulk-status-select {\n min-width: 130px;\n font-size: 13px;\n}\n\n/* Viewer-perspective badge (mockup 19) \u2014 surfaced in the header when the\n user has read-only access so the missing mutation controls don't feel\n broken. */\n.permission-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 999px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.permission-badge--viewer {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Tags section on the list detail header (Phase 4.2). */\n.list-detail__tags {\n margin-top: 8px;\n}\n\n/* Grid + side stats two-column layout (Phase 5.4). Stats hides on\n narrow screens so the grid keeps its breathing room. */\n.grid-with-stats {\n display: grid;\n grid-template-columns: 1fr 280px;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.list-detail__stats {\n min-width: 0;\n overflow-y: auto;\n}\n\n@media (max-width: 900px) {\n .grid-with-stats {\n grid-template-columns: 1fr;\n }\n .list-detail__stats {\n display: none;\n }\n}\n\n/* Custom Toolbar */\n.custom-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-bottom: none;\n border-radius: 8px 8px 0 0;\n flex-shrink: 0;\n}\n\n.toolbar-left {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.row-count {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.selection-count {\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n\n.toolbar-button span {\n font-size: 14px;\n}\n\n.remove-btn {\n color: var(--mj-status-error);\n}\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 200px;\n}\n\n.grid-container {\n flex: 1;\n min-height: 0;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 0 0 8px 8px;\n overflow: hidden;\n}\n\n/* Add Dropdown Button */\n.add-dropdown-wrapper {\n position: relative;\n}\n\n.add-btn {\n font-size: 16px;\n padding: 8px 25px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.add-dropdown-arrow {\n font-size: 12px;\n margin-left: 4px;\n}\n\n.add-dropdown-panel {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 240px;\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n z-index: 100;\n overflow: hidden;\n}\n\n.add-dropdown-item {\n display: flex;\n flex-direction: column;\n padding: 10px 16px;\n cursor: pointer;\n transition: background-color 0.15s;\n}\n\n.add-dropdown-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.add-dropdown-item-text {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.add-dropdown-item-desc {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n}\n\n/* ==========================================\n Dialog Styles\n ========================================== */\n\n.dialog-content {\n padding: 8px 0;\n}\n\n.dialog-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n gap: 20px;\n}\n\n.dialog-message {\n text-align: center;\n padding: 20px;\n}\n\n.warning-icon {\n font-size: 48px;\n color: var(--mj-status-warning);\n margin-bottom: 16px;\n display: block;\n}\n\n.dialog-message p {\n margin: 8px 0;\n font-size: 15px;\n color: var(--mj-text-primary);\n}\n\n.dialog-note {\n color: var(--mj-text-secondary) !important;\n font-size: 13px !important;\n}\n\n.progress-bar {\n width: 100%;\n max-width: 300px;\n}\n\n/* ==========================================\n Add Records Dialog\n ========================================== */\n\n.add-records-dialog {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.search-section {\n padding-bottom: 12px;\n border-bottom: 1px solid var(--mj-border-default);\n margin-bottom: 12px;\n}\n\n.search-input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.search-input-wrapper .search-icon {\n position: absolute;\n left: 10px;\n color: var(--mj-text-disabled);\n font-size: 14px;\n pointer-events: none;\n}\n\n.search-input-wrapper .search-input {\n width: 100%;\n padding-left: 32px;\n}\n\n.search-hint {\n display: block;\n font-size: 12px;\n color: var(--mj-text-disabled);\n margin-top: 6px;\n}\n\n.records-list {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.list-loading {\n display: flex;\n justify-content: center;\n padding: 40px;\n}\n\n.selection-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-default);\n margin-bottom: 8px;\n}\n\n.selection-info {\n margin-left: auto;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.records-scroll {\n flex: 1;\n overflow-y: auto;\n max-height: 280px;\n}\n\n.record-item {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.15s;\n gap: 12px;\n}\n\n.record-item:hover:not(.in-list) {\n background: var(--mj-bg-surface-card);\n}\n\n.record-item.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.record-item.in-list {\n background: var(--mj-bg-surface-card);\n cursor: default;\n opacity: 0.7;\n}\n\n.record-checkbox {\n width: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.record-checkbox input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n\n.in-list-icon {\n color: var(--mj-status-success);\n font-size: 14px;\n}\n\n.record-name {\n flex: 1;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.in-list-badge {\n font-size: 11px;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n border-radius: 10px;\n font-weight: 500;\n}\n\n/* ==========================================\n Add From View Dialog\n ========================================== */\n\n.dialog-instruction {\n margin: 0 0 16px 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n.views-list {\n max-height: 320px;\n overflow-y: auto;\n}\n\n.view-item {\n display: flex;\n align-items: center;\n padding: 12px;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.15s;\n gap: 12px;\n}\n\n.view-item:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.view-item.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.view-item input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n\n.view-icon {\n color: var(--mj-text-secondary);\n font-size: 16px;\n}\n\n.view-name {\n flex: 1;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n/* Export dialog (Phase 5.1, mockup 26) */\n.export-dialog { display: flex; flex-direction: column; gap: 8px; }\n.export-section-label {\n display: block;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n margin-bottom: 8px;\n}\n.export-format-row { display: flex; gap: 8px; }\n.export-format-option {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n cursor: pointer;\n font-size: 13px;\n background: var(--mj-bg-surface);\n}\n.export-format-option--selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, transparent);\n}\n.export-format-option input[type=\"radio\"] { margin: 0; }\n.export-section-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 16px 0;\n}\n.export-fields-list {\n max-height: 280px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n}\n.export-field-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 13px;\n cursor: pointer;\n}\n.export-field-row:last-child { border-bottom: none; }\n.export-field-row:hover { background: var(--mj-bg-surface-hover); }\n.export-field-actions {\n display: flex;\n gap: 12px;\n margin-top: 8px;\n align-items: center;\n font-size: 11.5px;\n}\n.export-field-actions a { color: var(--mj-text-link); }\n.export-field-actions .muted {\n margin-left: auto;\n color: var(--mj-text-muted);\n}\n"] }]
2197
2833
  }], () => [{ type: i1.SharedService }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i2.ExportService }, { type: i3.ListSharingService }], { ListID: [{
2198
2834
  type: Input
2199
- }], listDetailGrid: [{
2835
+ }], ListDetailGrid: [{
2200
2836
  type: ViewChild,
2201
2837
  args: ['listDetailGrid']
2202
2838
  }], onDocumentClick: [{