@memberjunction/ng-explorer-settings 5.44.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/user-app-config-reorder.test.d.ts +2 -0
- package/dist/__tests__/user-app-config-reorder.test.d.ts.map +1 -0
- package/dist/__tests__/user-app-config-reorder.test.js +66 -0
- package/dist/__tests__/user-app-config-reorder.test.js.map +1 -0
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts +29 -0
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts.map +1 -0
- package/dist/lib/user-app-config/user-app-config-reorder.js +43 -0
- package/dist/lib/user-app-config/user-app-config-reorder.js.map +1 -0
- package/dist/lib/user-app-config/user-app-config.component.d.ts +6 -3
- package/dist/lib/user-app-config/user-app-config.component.d.ts.map +1 -1
- package/dist/lib/user-app-config/user-app-config.component.js +18 -21
- package/dist/lib/user-app-config/user-app-config.component.js.map +1 -1
- package/package.json +19 -19
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-app-config-reorder.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/user-app-config-reorder.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Covers the app-config dialog's positional reorder/resequence helpers.
|
|
3
|
+
*
|
|
4
|
+
* Regression for issue #3027: rows can carry DUPLICATE Sequence values (e.g. a
|
|
5
|
+
* re-enabled app kept a stale value), and the old value-swap MoveUp/MoveDown swapped
|
|
6
|
+
* two equal numbers — rows went dirty, Save enabled, and the order silently never
|
|
7
|
+
* changed. Positional moves must reorder regardless of the stored values and heal
|
|
8
|
+
* duplicates by renumbering 0..n-1.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { moveAndResequence, resequenceItems } from '../lib/user-app-config/user-app-config-reorder';
|
|
12
|
+
function item(name, sequence) {
|
|
13
|
+
return { name, sequence, isDirty: false };
|
|
14
|
+
}
|
|
15
|
+
const names = (items) => items.map(i => i.name).join(',');
|
|
16
|
+
const seqs = (items) => items.map(i => i.sequence).join(',');
|
|
17
|
+
describe('moveAndResequence', () => {
|
|
18
|
+
it('reorders and renumbers with clean sequential values', () => {
|
|
19
|
+
const result = moveAndResequence([item('a', 0), item('b', 1), item('c', 2)], 2, 1);
|
|
20
|
+
expect(names(result)).toBe('a,c,b');
|
|
21
|
+
expect(seqs(result)).toBe('0,1,2');
|
|
22
|
+
});
|
|
23
|
+
it('reorders even when the two neighbors hold DUPLICATE sequence values (the #3027 no-op case)', () => {
|
|
24
|
+
// Old value-swap behavior: swapping 6 and 6 changed nothing — order stuck forever.
|
|
25
|
+
const result = moveAndResequence([item('a', 0), item('bulk', 6), item('testing', 6)], 2, 1);
|
|
26
|
+
expect(names(result)).toBe('a,testing,bulk');
|
|
27
|
+
expect(seqs(result)).toBe('0,1,2'); // duplicates healed
|
|
28
|
+
expect(result.every(i => i.sequence !== 6 || i.isDirty)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('reorders across a stale 999 park value and heals it', () => {
|
|
31
|
+
const result = moveAndResequence([item('a', 0), item('b', 1), item('reenabled', 999)], 2, 0);
|
|
32
|
+
expect(names(result)).toBe('reenabled,a,b');
|
|
33
|
+
expect(seqs(result)).toBe('0,1,2');
|
|
34
|
+
});
|
|
35
|
+
it('marks exactly the renumbered items dirty', () => {
|
|
36
|
+
const result = moveAndResequence([item('a', 0), item('b', 1), item('c', 2)], 2, 1);
|
|
37
|
+
expect(result.map(i => i.isDirty)).toEqual([false, true, true]); // a keeps 0, c→1 and b→2 changed
|
|
38
|
+
});
|
|
39
|
+
it('returns the input unchanged for out-of-range or no-op moves', () => {
|
|
40
|
+
const input = [item('a', 5), item('b', 5)];
|
|
41
|
+
expect(moveAndResequence(input, -1, 0)).toBe(input);
|
|
42
|
+
expect(moveAndResequence(input, 0, 2)).toBe(input);
|
|
43
|
+
expect(moveAndResequence(input, 1, 1)).toBe(input);
|
|
44
|
+
expect(seqs(input)).toBe('5,5'); // untouched — no surprise renumbering on a no-op
|
|
45
|
+
});
|
|
46
|
+
it('does not mutate the input array order (returns a new array)', () => {
|
|
47
|
+
const input = [item('a', 0), item('b', 1)];
|
|
48
|
+
const result = moveAndResequence(input, 1, 0);
|
|
49
|
+
expect(names(input)).toBe('a,b');
|
|
50
|
+
expect(result).not.toBe(input);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('resequenceItems', () => {
|
|
54
|
+
it('renumbers duplicates in display order and dirties only corrected items', () => {
|
|
55
|
+
const items = [item('a', 0), item('b', 6), item('c', 6), item('d', 999)];
|
|
56
|
+
resequenceItems(items);
|
|
57
|
+
expect(seqs(items)).toBe('0,1,2,3');
|
|
58
|
+
expect(items.map(i => i.isDirty)).toEqual([false, true, true, true]);
|
|
59
|
+
});
|
|
60
|
+
it('is a no-op on an already-sequential list', () => {
|
|
61
|
+
const items = [item('a', 0), item('b', 1)];
|
|
62
|
+
resequenceItems(items);
|
|
63
|
+
expect(items.every(i => !i.isDirty)).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=user-app-config-reorder.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-app-config-reorder.test.js","sourceRoot":"","sources":["../../src/__tests__/user-app-config-reorder.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Covers the app-config dialog's positional reorder/resequence helpers.\n *\n * Regression for issue #3027: rows can carry DUPLICATE Sequence values (e.g. a\n * re-enabled app kept a stale value), and the old value-swap MoveUp/MoveDown swapped\n * two equal numbers — rows went dirty, Save enabled, and the order silently never\n * changed. Positional moves must reorder regardless of the stored values and heal\n * duplicates by renumbering 0..n-1.\n */\nimport { describe, it, expect } from 'vitest';\nimport { moveAndResequence, resequenceItems, ReorderableAppItem } from '../lib/user-app-config/user-app-config-reorder';\n\ninterface TestItem extends ReorderableAppItem {\n name: string;\n}\n\nfunction item(name: string, sequence: number): TestItem {\n return { name, sequence, isDirty: false };\n}\n\nconst names = (items: TestItem[]) => items.map(i => i.name).join(',');\nconst seqs = (items: TestItem[]) => items.map(i => i.sequence).join(',');\n\ndescribe('moveAndResequence', () => {\n it('reorders and renumbers with clean sequential values', () => {\n const result = moveAndResequence([item('a', 0), item('b', 1), item('c', 2)], 2, 1);\n expect(names(result)).toBe('a,c,b');\n expect(seqs(result)).toBe('0,1,2');\n });\n\n it('reorders even when the two neighbors hold DUPLICATE sequence values (the #3027 no-op case)', () => {\n // Old value-swap behavior: swapping 6 and 6 changed nothing — order stuck forever.\n const result = moveAndResequence([item('a', 0), item('bulk', 6), item('testing', 6)], 2, 1);\n expect(names(result)).toBe('a,testing,bulk');\n expect(seqs(result)).toBe('0,1,2'); // duplicates healed\n expect(result.every(i => i.sequence !== 6 || i.isDirty)).toBe(true);\n });\n\n it('reorders across a stale 999 park value and heals it', () => {\n const result = moveAndResequence([item('a', 0), item('b', 1), item('reenabled', 999)], 2, 0);\n expect(names(result)).toBe('reenabled,a,b');\n expect(seqs(result)).toBe('0,1,2');\n });\n\n it('marks exactly the renumbered items dirty', () => {\n const result = moveAndResequence([item('a', 0), item('b', 1), item('c', 2)], 2, 1);\n expect(result.map(i => i.isDirty)).toEqual([false, true, true]); // a keeps 0, c→1 and b→2 changed\n });\n\n it('returns the input unchanged for out-of-range or no-op moves', () => {\n const input = [item('a', 5), item('b', 5)];\n expect(moveAndResequence(input, -1, 0)).toBe(input);\n expect(moveAndResequence(input, 0, 2)).toBe(input);\n expect(moveAndResequence(input, 1, 1)).toBe(input);\n expect(seqs(input)).toBe('5,5'); // untouched — no surprise renumbering on a no-op\n });\n\n it('does not mutate the input array order (returns a new array)', () => {\n const input = [item('a', 0), item('b', 1)];\n const result = moveAndResequence(input, 1, 0);\n expect(names(input)).toBe('a,b');\n expect(result).not.toBe(input);\n });\n});\n\ndescribe('resequenceItems', () => {\n it('renumbers duplicates in display order and dirties only corrected items', () => {\n const items = [item('a', 0), item('b', 6), item('c', 6), item('d', 999)];\n resequenceItems(items);\n expect(seqs(items)).toBe('0,1,2,3');\n expect(items.map(i => i.isDirty)).toEqual([false, true, true, true]);\n });\n\n it('is a no-op on an already-sequential list', () => {\n const items = [item('a', 0), item('b', 1)];\n resequenceItems(items);\n expect(items.every(i => !i.isDirty)).toBe(true);\n });\n});\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure reorder/resequence helpers for the user app-config dialog.
|
|
3
|
+
*
|
|
4
|
+
* Reordering is POSITIONAL (splice + renumber 0..n-1), never a sequence-value swap:
|
|
5
|
+
* persisted rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled
|
|
6
|
+
* app kept a stale value like the dialog's 999 park value), and swapping two equal
|
|
7
|
+
* values marks rows dirty without changing the sort order — the user clicks the
|
|
8
|
+
* arrows, saves, and nothing happens. Positional moves always work, and the renumber
|
|
9
|
+
* heals any duplicates as a side effect.
|
|
10
|
+
*
|
|
11
|
+
* Kept pure (no Angular imports) so vitest covers them without a TestBed.
|
|
12
|
+
*/
|
|
13
|
+
export interface ReorderableAppItem {
|
|
14
|
+
sequence: number;
|
|
15
|
+
isDirty: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Returns a new array with the item at `fromIndex` moved to `toIndex`, renumbered
|
|
19
|
+
* 0..n-1 in the new display order. Out-of-range indexes return the input array
|
|
20
|
+
* unchanged (and un-renumbered) so callers can bind arrow buttons without guards.
|
|
21
|
+
*/
|
|
22
|
+
export declare function moveAndResequence<T extends ReorderableAppItem>(items: T[], fromIndex: number, toIndex: number): T[];
|
|
23
|
+
/**
|
|
24
|
+
* Renumbers items to their display index (0..n-1), marking every corrected item
|
|
25
|
+
* dirty so the next save persists the fix — this is what self-heals duplicate
|
|
26
|
+
* Sequence values already in the database.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resequenceItems<T extends ReorderableAppItem>(items: T[]): void;
|
|
29
|
+
//# sourceMappingURL=user-app-config-reorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-app-config-reorder.d.ts","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config-reorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,EAAE,CAanH;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,CAO9E"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure reorder/resequence helpers for the user app-config dialog.
|
|
3
|
+
*
|
|
4
|
+
* Reordering is POSITIONAL (splice + renumber 0..n-1), never a sequence-value swap:
|
|
5
|
+
* persisted rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled
|
|
6
|
+
* app kept a stale value like the dialog's 999 park value), and swapping two equal
|
|
7
|
+
* values marks rows dirty without changing the sort order — the user clicks the
|
|
8
|
+
* arrows, saves, and nothing happens. Positional moves always work, and the renumber
|
|
9
|
+
* heals any duplicates as a side effect.
|
|
10
|
+
*
|
|
11
|
+
* Kept pure (no Angular imports) so vitest covers them without a TestBed.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Returns a new array with the item at `fromIndex` moved to `toIndex`, renumbered
|
|
15
|
+
* 0..n-1 in the new display order. Out-of-range indexes return the input array
|
|
16
|
+
* unchanged (and un-renumbered) so callers can bind arrow buttons without guards.
|
|
17
|
+
*/
|
|
18
|
+
export function moveAndResequence(items, fromIndex, toIndex) {
|
|
19
|
+
if (fromIndex < 0 || fromIndex >= items.length ||
|
|
20
|
+
toIndex < 0 || toIndex >= items.length ||
|
|
21
|
+
fromIndex === toIndex) {
|
|
22
|
+
return items;
|
|
23
|
+
}
|
|
24
|
+
const reordered = [...items];
|
|
25
|
+
const [moved] = reordered.splice(fromIndex, 1);
|
|
26
|
+
reordered.splice(toIndex, 0, moved);
|
|
27
|
+
resequenceItems(reordered);
|
|
28
|
+
return reordered;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Renumbers items to their display index (0..n-1), marking every corrected item
|
|
32
|
+
* dirty so the next save persists the fix — this is what self-heals duplicate
|
|
33
|
+
* Sequence values already in the database.
|
|
34
|
+
*/
|
|
35
|
+
export function resequenceItems(items) {
|
|
36
|
+
items.forEach((item, index) => {
|
|
37
|
+
if (item.sequence !== index) {
|
|
38
|
+
item.sequence = index;
|
|
39
|
+
item.isDirty = true;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=user-app-config-reorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-app-config-reorder.js","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config-reorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAA+B,KAAU,EAAE,SAAiB,EAAE,OAAe;IAC5G,IACE,SAAS,GAAG,CAAC,IAAI,SAAS,IAAI,KAAK,CAAC,MAAM;QAC1C,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM;QACtC,SAAS,KAAK,OAAO,EACrB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC/C,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IACpC,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAA+B,KAAU;IACtE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5B,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Pure reorder/resequence helpers for the user app-config dialog.\n *\n * Reordering is POSITIONAL (splice + renumber 0..n-1), never a sequence-value swap:\n * persisted rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled\n * app kept a stale value like the dialog's 999 park value), and swapping two equal\n * values marks rows dirty without changing the sort order — the user clicks the\n * arrows, saves, and nothing happens. Positional moves always work, and the renumber\n * heals any duplicates as a side effect.\n *\n * Kept pure (no Angular imports) so vitest covers them without a TestBed.\n */\n\nexport interface ReorderableAppItem {\n sequence: number;\n isDirty: boolean;\n}\n\n/**\n * Returns a new array with the item at `fromIndex` moved to `toIndex`, renumbered\n * 0..n-1 in the new display order. Out-of-range indexes return the input array\n * unchanged (and un-renumbered) so callers can bind arrow buttons without guards.\n */\nexport function moveAndResequence<T extends ReorderableAppItem>(items: T[], fromIndex: number, toIndex: number): T[] {\n if (\n fromIndex < 0 || fromIndex >= items.length ||\n toIndex < 0 || toIndex >= items.length ||\n fromIndex === toIndex\n ) {\n return items;\n }\n const reordered = [...items];\n const [moved] = reordered.splice(fromIndex, 1);\n reordered.splice(toIndex, 0, moved);\n resequenceItems(reordered);\n return reordered;\n}\n\n/**\n * Renumbers items to their display index (0..n-1), marking every corrected item\n * dirty so the next save persists the fix — this is what self-heals duplicate\n * Sequence values already in the database.\n */\nexport function resequenceItems<T extends ReorderableAppItem>(items: T[]): void {\n items.forEach((item, index) => {\n if (item.sequence !== index) {\n item.sequence = index;\n item.isDirty = true;\n }\n });\n}\n"]}
|
|
@@ -62,13 +62,17 @@ export declare class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
62
62
|
*/
|
|
63
63
|
ToggleApp(item: AppConfigItem): void;
|
|
64
64
|
/**
|
|
65
|
-
* Moves an app up in the order
|
|
65
|
+
* Moves an app up in the order. Positional (splice + renumber), NOT a sequence-value
|
|
66
|
+
* swap: rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled app
|
|
67
|
+
* kept a stale value), and swapping two equal values is a silent no-op. Reordering by
|
|
68
|
+
* position then renumbering 0..n-1 always works and heals duplicates as a side effect.
|
|
66
69
|
*/
|
|
67
70
|
MoveUp(item: AppConfigItem): void;
|
|
68
71
|
/**
|
|
69
|
-
* Moves an app down in the order
|
|
72
|
+
* Moves an app down in the order. Positional — see {@link MoveUp} for why.
|
|
70
73
|
*/
|
|
71
74
|
MoveDown(item: AppConfigItem): void;
|
|
75
|
+
private moveActiveApp;
|
|
72
76
|
/**
|
|
73
77
|
* Checks if there are any unsaved changes
|
|
74
78
|
*/
|
|
@@ -104,7 +108,6 @@ export declare class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
104
108
|
private buildAppConfigItems;
|
|
105
109
|
private refreshAppLists;
|
|
106
110
|
private resequenceActiveApps;
|
|
107
|
-
private swapSequences;
|
|
108
111
|
private updateUserApplication;
|
|
109
112
|
private createUserApplication;
|
|
110
113
|
static ɵfac: i0.ɵɵFactoryDeclaration<UserAppConfigComponent, never>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-app-config.component.d.ts","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAiD,MAAM,eAAe,CAAC;AAGtH,OAAO,EAAsB,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAI1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;
|
|
1
|
+
{"version":3,"file":"user-app-config.component.d.ts","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAiD,MAAM,eAAe,CAAC;AAGtH,OAAO,EAAsB,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAI1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AAErE;;GAEG;AACH,UAAU,aAAa;IACrB,GAAG,EAAE,eAAe,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBAMa,sBAAuB,SAAQ,oBAAoB;IAC9D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,EAAE,CAAsB;IAEhC,OAAO,CAAC,WAAW,CAAS;IAE5B,IACI,UAAU,CAAC,KAAK,EAAE,OAAO,EAI5B;IACD,IAAI,UAAU,IAAI,OAAO,CAExB;IAES,gBAAgB,wBAA+B;IAC/C,WAAW,qBAA4B;IAEjD,OAAO,EAAE,aAAa,EAAE,CAAM;IAC9B,UAAU,EAAE,aAAa,EAAE,CAAM;IACjC,aAAa,EAAE,aAAa,EAAE,CAAM;IAEpC,SAAS,UAAS;IAClB,QAAQ,UAAS;IACjB,YAAY,SAAM;IAElB,uBAAuB,UAAS;IAChC,sBAAsB,UAAS;IAE/B,WAAW,EAAE,aAAa,GAAG,IAAI,CAAQ;IACzC,YAAY,SAAM;IAClB,eAAe,SAAM;IAErB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAOjC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IASpC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAQpC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAOjC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAOnC,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoD3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAUvE,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAOlC,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKlD,SAAS,IAAI,IAAI;IAMjB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAiB9B,cAAc,SAAM;IACpB,cAAc,SAAM;IACpB,eAAe,UAAS;IAExB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,gBAAgB,CAA4B;IACpD,OAAO,CAAC,oBAAoB,CAA4B;IAExD,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAkBxD,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAexC,cAAc,IAAI,IAAI;IAmBtB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,mBAAmB;YAYb,iBAAiB;IA2B/B,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,oBAAoB;YAKd,qBAAqB;YAgBrB,qBAAqB;yCA7YxB,sBAAsB;2CAAtB,sBAAsB;CA+ZlC"}
|
|
@@ -5,6 +5,7 @@ import { ApplicationManager } from '@memberjunction/ng-base-application';
|
|
|
5
5
|
import { SharedService } from '@memberjunction/ng-shared';
|
|
6
6
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
7
7
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
8
|
+
import { moveAndResequence, resequenceItems } from './user-app-config-reorder';
|
|
8
9
|
import * as i0 from "@angular/core";
|
|
9
10
|
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
10
11
|
import * as i2 from "@memberjunction/ng-ui-components";
|
|
@@ -450,25 +451,29 @@ export class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
450
451
|
}
|
|
451
452
|
}
|
|
452
453
|
/**
|
|
453
|
-
* Moves an app up in the order
|
|
454
|
+
* Moves an app up in the order. Positional (splice + renumber), NOT a sequence-value
|
|
455
|
+
* swap: rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled app
|
|
456
|
+
* kept a stale value), and swapping two equal values is a silent no-op. Reordering by
|
|
457
|
+
* position then renumbering 0..n-1 always works and heals duplicates as a side effect.
|
|
454
458
|
*/
|
|
455
459
|
MoveUp(item) {
|
|
456
460
|
const index = this.ActiveApps.indexOf(item);
|
|
457
461
|
if (index > 0) {
|
|
458
|
-
this.
|
|
459
|
-
this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);
|
|
462
|
+
this.moveActiveApp(index, index - 1);
|
|
460
463
|
}
|
|
461
464
|
}
|
|
462
465
|
/**
|
|
463
|
-
* Moves an app down in the order
|
|
466
|
+
* Moves an app down in the order. Positional — see {@link MoveUp} for why.
|
|
464
467
|
*/
|
|
465
468
|
MoveDown(item) {
|
|
466
469
|
const index = this.ActiveApps.indexOf(item);
|
|
467
|
-
if (index < this.ActiveApps.length - 1) {
|
|
468
|
-
this.
|
|
469
|
-
this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);
|
|
470
|
+
if (index >= 0 && index < this.ActiveApps.length - 1) {
|
|
471
|
+
this.moveActiveApp(index, index + 1);
|
|
470
472
|
}
|
|
471
473
|
}
|
|
474
|
+
moveActiveApp(fromIndex, toIndex) {
|
|
475
|
+
this.ActiveApps = moveAndResequence(this.ActiveApps, fromIndex, toIndex);
|
|
476
|
+
}
|
|
472
477
|
/**
|
|
473
478
|
* Checks if there are any unsaved changes
|
|
474
479
|
*/
|
|
@@ -486,6 +491,10 @@ export class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
486
491
|
this.IsSaving = true;
|
|
487
492
|
this.ErrorMessage = '';
|
|
488
493
|
try {
|
|
494
|
+
// Self-heal: renumber active apps 0..n-1 before persisting. Existing rows can
|
|
495
|
+
// carry duplicate Sequence values from older data (issue #3027); renumbering in
|
|
496
|
+
// display order marks any corrected rows dirty so this save fixes them for good.
|
|
497
|
+
this.resequenceActiveApps();
|
|
489
498
|
const md = this.ProviderToUse;
|
|
490
499
|
for (const item of this.AllApps) {
|
|
491
500
|
if (!item.isDirty)
|
|
@@ -692,19 +701,7 @@ export class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
692
701
|
.sort((a, b) => a.app.Name.localeCompare(b.app.Name));
|
|
693
702
|
}
|
|
694
703
|
resequenceActiveApps() {
|
|
695
|
-
this.ActiveApps
|
|
696
|
-
if (item.sequence !== index) {
|
|
697
|
-
item.sequence = index;
|
|
698
|
-
item.isDirty = true;
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
swapSequences(a, b) {
|
|
703
|
-
const tempSeq = a.sequence;
|
|
704
|
-
a.sequence = b.sequence;
|
|
705
|
-
b.sequence = tempSeq;
|
|
706
|
-
a.isDirty = true;
|
|
707
|
-
b.isDirty = true;
|
|
704
|
+
resequenceItems(this.ActiveApps);
|
|
708
705
|
}
|
|
709
706
|
async updateUserApplication(md, item) {
|
|
710
707
|
const userApp = await md.GetEntityObject('MJ: User Applications');
|
|
@@ -750,5 +747,5 @@ export class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
750
747
|
}], ConfigSaved: [{
|
|
751
748
|
type: Output
|
|
752
749
|
}] }); })();
|
|
753
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserAppConfigComponent, { className: "UserAppConfigComponent", filePath: "src/lib/user-app-config/user-app-config.component.ts", lineNumber:
|
|
750
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserAppConfigComponent, { className: "UserAppConfigComponent", filePath: "src/lib/user-app-config/user-app-config.component.ts", lineNumber: 34 }); })();
|
|
754
751
|
//# sourceMappingURL=user-app-config.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-app-config.component.js","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config.component.ts","../../../src/lib/user-app-config/user-app-config.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ChangeDetectorRef, NgZone, ElementRef, inject } from '@angular/core';\nimport { Metadata, LogError, LogStatus, IMetadataProvider, LogStatusEx } from '@memberjunction/core';\nimport { MJUserApplicationEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n/**\n * Represents an app item in the configuration UI\n */\ninterface AppConfigItem {\n app: BaseApplication;\n userAppId: string | null;\n sequence: number;\n isActive: boolean;\n isDirty: boolean;\n}\n\n/**\n * Full-screen modal dialog for configuring user's application visibility and order.\n * Allows users to:\n * - Select which applications to show in the app switcher\n * - Reorder applications via drag-and-drop (desktop)\n * - Toggle apps on/off in a single list (mobile)\n */\n@Component({\n standalone: false,\n selector: 'mj-user-app-config',\n templateUrl: './user-app-config.component.html',\n styleUrls: ['./user-app-config.component.css']\n})\nexport class UserAppConfigComponent extends BaseAngularComponent {\n private appManager = inject(ApplicationManager);\n private sharedService = inject(SharedService);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private el = inject(ElementRef);\n\n private _showDialog = false;\n\n @Input()\n set ShowDialog(value: boolean) {\n if (value !== this._showDialog) {\n this._showDialog = value;\n }\n }\n get ShowDialog(): boolean {\n return this._showDialog;\n }\n\n @Output() ShowDialogChange = new EventEmitter<boolean>();\n @Output() ConfigSaved = new EventEmitter<void>();\n\n AllApps: AppConfigItem[] = [];\n ActiveApps: AppConfigItem[] = [];\n AvailableApps: AppConfigItem[] = [];\n\n IsLoading = false;\n IsSaving = false;\n ErrorMessage = '';\n\n AvailablePanelCollapsed = false;\n SelectedPanelCollapsed = false;\n\n DraggedItem: AppConfigItem | null = null;\n DraggedIndex = -1;\n DropTargetIndex = -1;\n\n /**\n * Opens the dialog and loads user's app configuration\n */\n async Open(): Promise<void> {\n this._showDialog = true;\n this.ShowDialogChange.emit(true);\n this.ErrorMessage = '';\n await this.loadConfiguration();\n }\n\n /**\n * Closes the dialog without saving\n */\n Close(): void {\n this._showDialog = false;\n this.ShowDialogChange.emit(false);\n }\n\n /**\n * Adds an app to the user's active list\n */\n AddApp(item: AppConfigItem): void {\n item.isActive = true;\n item.sequence = this.ActiveApps.length;\n item.isDirty = true;\n this.refreshAppLists();\n }\n\n /**\n * Removes an app from the user's active list\n */\n RemoveApp(item: AppConfigItem): void {\n item.isActive = false;\n item.sequence = 999;\n item.isDirty = true;\n this.refreshAppLists();\n\n this.resequenceActiveApps();\n }\n\n /**\n * Toggles an app between active and inactive\n */\n ToggleApp(item: AppConfigItem): void {\n if (item.isActive) {\n this.RemoveApp(item);\n } else {\n this.AddApp(item);\n }\n }\n\n /**\n * Moves an app up in the order\n */\n MoveUp(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index > 0) {\n this.swapSequences(item, this.ActiveApps[index - 1]);\n this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);\n }\n }\n\n /**\n * Moves an app down in the order\n */\n MoveDown(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index < this.ActiveApps.length - 1) {\n this.swapSequences(item, this.ActiveApps[index + 1]);\n this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);\n }\n }\n\n /**\n * Checks if there are any unsaved changes\n */\n HasChanges(): boolean {\n return this.AllApps.some(item => item.isDirty);\n }\n\n /**\n * Saves the user's app configuration\n */\n async Save(): Promise<void> {\n if (!this.HasChanges()) {\n this.Close();\n return;\n }\n\n this.IsSaving = true;\n this.ErrorMessage = '';\n\n try {\n const md = this.ProviderToUse;\n\n for (const item of this.AllApps) {\n if (!item.isDirty) continue;\n\n if (item.userAppId) {\n await this.updateUserApplication(md, item);\n } else if (item.isActive) {\n await this.createUserApplication(md, item);\n }\n }\n\n // Each userApp.Save() above fires a BaseEntity 'save' event, which UserInfoEngine\n // catches via HandleIndividualBaseEntityEvent → debounced refresh → NotifyDataChange.\n // ApplicationManager.subscribeToEngineChanges then propagates to the shell app\n // switcher. No explicit reload needed here — and avoiding it sidesteps NG0100\n // from observable emissions interleaving with this method's save loop.\n LogStatusEx({\n message: 'User app configuration saved',\n verboseOnly: true \n });\n\n this.sharedService.CreateSimpleNotification('App configuration saved successfully!', 'success', 3000);\n this.ConfigSaved.emit();\n this.Close();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to save configuration. Please try again.';\n LogError('Error saving app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsSaving = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n /**\n * Resets all changes and reloads the configuration\n */\n async Reset(): Promise<void> {\n await this.loadConfiguration();\n }\n\n // ---------------------------------------------------------------------------\n // Drag-and-drop handlers (desktop only)\n // ---------------------------------------------------------------------------\n\n OnDragStart(event: DragEvent, item: AppConfigItem, index: number): void {\n this.DraggedItem = item;\n this.DraggedIndex = index;\n\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', index.toString());\n }\n }\n\n OnDragOver(event: DragEvent): void {\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move';\n }\n }\n\n OnDragEnter(event: DragEvent, index: number): void {\n event.preventDefault();\n this.DropTargetIndex = index;\n }\n\n OnDragEnd(): void {\n this.DraggedItem = null;\n this.DraggedIndex = -1;\n this.DropTargetIndex = -1;\n }\n\n OnDrop(event: DragEvent): void {\n event.preventDefault();\n\n if (this.DraggedIndex >= 0 && this.DropTargetIndex >= 0 && this.DraggedIndex !== this.DropTargetIndex) {\n const [movedItem] = this.ActiveApps.splice(this.DraggedIndex, 1);\n this.ActiveApps.splice(this.DropTargetIndex, 0, movedItem);\n this.resequenceActiveApps();\n this.cdr.detectChanges();\n }\n\n this.OnDragEnd();\n }\n\n // ---------------------------------------------------------------------------\n // Touch drag-and-drop handlers (mobile)\n // ---------------------------------------------------------------------------\n\n TouchDragIndex = -1;\n TouchDropIndex = -1;\n TouchDragActive = false;\n\n private touchStartY = 0;\n private touchCurrentY = 0;\n private touchRowHeight = 0;\n private touchDragElement: HTMLElement | null = null;\n private touchScrollContainer: HTMLElement | null = null;\n\n OnTouchDragStart(event: TouchEvent, index: number): void {\n const touch = event.touches[0];\n const row = (event.target as HTMLElement).closest('.mobile-app-row') as HTMLElement | null;\n if (!row) return;\n\n event.preventDefault();\n this.TouchDragIndex = index;\n this.TouchDropIndex = index;\n this.TouchDragActive = true;\n this.touchStartY = touch.clientY;\n this.touchCurrentY = touch.clientY;\n this.touchDragElement = row;\n this.touchRowHeight = row.offsetHeight;\n this.touchScrollContainer = this.el.nativeElement.querySelector('.mobile-list');\n\n row.classList.add('touch-dragging');\n }\n\n OnTouchDragMove(event: TouchEvent): void {\n if (!this.TouchDragActive || !this.touchDragElement) return;\n\n event.preventDefault();\n const touch = event.touches[0];\n this.touchCurrentY = touch.clientY;\n const deltaY = this.touchCurrentY - this.touchStartY;\n\n this.touchDragElement.style.transform = `translateY(${deltaY}px)`;\n this.touchDragElement.style.zIndex = '100';\n\n this.updateTouchDropTarget(deltaY);\n this.autoScrollIfNeeded();\n }\n\n OnTouchDragEnd(): void {\n if (!this.TouchDragActive) return;\n\n if (this.touchDragElement) {\n this.touchDragElement.style.transform = '';\n this.touchDragElement.style.zIndex = '';\n this.touchDragElement.classList.remove('touch-dragging');\n }\n\n if (this.TouchDragIndex >= 0 && this.TouchDropIndex >= 0 && this.TouchDragIndex !== this.TouchDropIndex) {\n const [movedItem] = this.ActiveApps.splice(this.TouchDragIndex, 1);\n this.ActiveApps.splice(this.TouchDropIndex, 0, movedItem);\n this.resequenceActiveApps();\n }\n\n this.resetTouchDragState();\n this.cdr.detectChanges();\n }\n\n private updateTouchDropTarget(deltaY: number): void {\n const rowsToMove = Math.round(deltaY / this.touchRowHeight);\n const newIndex = Math.max(0, Math.min(this.ActiveApps.length - 1, this.TouchDragIndex + rowsToMove));\n this.TouchDropIndex = newIndex;\n }\n\n private autoScrollIfNeeded(): void {\n if (!this.touchScrollContainer) return;\n\n const rect = this.touchScrollContainer.getBoundingClientRect();\n const edgeZone = 40;\n\n if (this.touchCurrentY < rect.top + edgeZone) {\n this.touchScrollContainer.scrollTop -= 8;\n } else if (this.touchCurrentY > rect.bottom - edgeZone) {\n this.touchScrollContainer.scrollTop += 8;\n }\n }\n\n private resetTouchDragState(): void {\n this.TouchDragIndex = -1;\n this.TouchDropIndex = -1;\n this.TouchDragActive = false;\n this.touchDragElement = null;\n this.touchScrollContainer = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async loadConfiguration(): Promise<void> {\n this.IsLoading = true;\n this.ErrorMessage = '';\n\n try {\n // Read directly from UserInfoEngine's cache. The engine already loaded\n // and maintains MJ: User Applications via its event-driven refresh — no\n // need to re-query the DB on every dialog open. EnsureLoaded is idempotent\n // and instant when the engine is already loaded (the typical case here,\n // since UserInfoEngine fires at startup).\n await UserInfoEngine.Instance.EnsureLoaded();\n const systemApps = this.appManager.GetAuthorizedSystemApps();\n const userApps = UserInfoEngine.Instance.UserApplications;\n this.AllApps = this.buildAppConfigItems(systemApps, userApps);\n this.refreshAppLists();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to load app configuration. Please try again.';\n LogError('Error loading app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsLoading = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n private buildAppConfigItems(systemApps: BaseApplication[], userApps: MJUserApplicationEntity[]): AppConfigItem[] {\n return systemApps.map(app => {\n const userApp = userApps.find(ua => UUIDsEqual(ua.ApplicationID, app.ID));\n return {\n app,\n userAppId: userApp?.ID ?? null,\n sequence: userApp?.Sequence ?? 999,\n isActive: userApp?.IsActive ?? false,\n isDirty: false\n };\n });\n }\n\n private refreshAppLists(): void {\n this.ActiveApps = this.AllApps\n .filter(item => item.isActive)\n .sort((a, b) => a.sequence - b.sequence);\n\n this.AvailableApps = this.AllApps\n .filter(item => !item.isActive)\n .sort((a, b) => a.app.Name.localeCompare(b.app.Name));\n }\n\n private resequenceActiveApps(): void {\n this.ActiveApps.forEach((item, index) => {\n if (item.sequence !== index) {\n item.sequence = index;\n item.isDirty = true;\n }\n });\n }\n\n private swapSequences(a: AppConfigItem, b: AppConfigItem): void {\n const tempSeq = a.sequence;\n a.sequence = b.sequence;\n b.sequence = tempSeq;\n a.isDirty = true;\n b.isDirty = true;\n }\n\n private async updateUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n await userApp.Load(item.userAppId!);\n\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to update UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.isDirty = false;\n LogStatus(`Updated UserApplication for ${item.app.Name}: sequence=${item.sequence}, isActive=${item.isActive}`);\n }\n\n private async createUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n userApp.NewRecord();\n\n userApp.UserID = md.CurrentUser.ID;\n userApp.ApplicationID = item.app.ID;\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to create UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.userAppId = userApp.ID;\n item.isDirty = false;\n LogStatus(`Created UserApplication for ${item.app.Name}: sequence=${item.sequence}`);\n }\n}\n","<!-- Modal Backdrop -->\n@if (ShowDialog) {\n <div class=\"modal-backdrop\" (click)=\"Close()\" role=\"presentation\"></div>\n <div\n class=\"modal-dialog full-screen-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"app-config-title\"\n >\n <!-- Dialog Header -->\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" id=\"app-config-title\">\n <i class=\"fa-solid fa-grid-2\" aria-hidden=\"true\"></i>\n Configure Apps\n </h2>\n <button\n class=\"modal-close\"\n (click)=\"Close()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"app-config-container\">\n <!-- Loading State -->\n @if (IsLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\">\n <mj-loading text=\"Loading your app configuration...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Error Message -->\n @if (ErrorMessage) {\n <mj-alert Variant=\"error\">{{ ErrorMessage }}</mj-alert>\n }\n\n <!-- Main Content -->\n @if (!IsLoading) {\n <div class=\"config-content\">\n <p class=\"config-description\">\n Select which applications appear in your app switcher and arrange them in your preferred order.\n Double-click to quickly add or remove. Drag and drop to reorder, or use the arrow buttons.\n </p>\n\n <!-- Desktop: two-panel layout with drag-and-drop -->\n <div class=\"panels-container desktop-only\">\n <!-- Available Apps Panel -->\n <div class=\"panel available-apps-panel\" [class.collapsed]=\"AvailablePanelCollapsed\">\n <div class=\"panel-header\" (click)=\"AvailablePanelCollapsed = !AvailablePanelCollapsed\">\n <i class=\"fa-solid fa-layer-group\" aria-hidden=\"true\"></i>\n <h3>Available Applications</h3>\n <span class=\"badge\">{{ AvailableApps.length }}</span>\n <i\n class=\"collapse-toggle fa-solid\"\n [class.fa-chevron-down]=\"AvailablePanelCollapsed\"\n [class.fa-chevron-up]=\"!AvailablePanelCollapsed\"\n aria-hidden=\"true\"\n ></i>\n </div>\n\n <div class=\"panel-content\">\n @if (AvailableApps.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-check-double\"\n Title=\"All applications added!\" />\n }\n\n @for (item of AvailableApps; track item.app.ID) {\n <div class=\"available-app-item\" (dblclick)=\"AddApp(item)\">\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-info\">\n <span class=\"app-name\">{{ item.app.Name }}</span>\n @if (item.app.Description) {\n <span class=\"app-description\">{{ item.app.Description }}</span>\n }\n </div>\n\n <button\n mjButton variant=\"outline\" size=\"sm\"\n (click)=\"AddApp(item)\"\n title=\"Add to your apps\"\n [attr.aria-label]=\"'Add ' + item.app.Name\"\n >\n <i class=\"fa-solid fa-plus\" aria-hidden=\"true\"></i>\n Add\n </button>\n </div>\n }\n </div>\n </div>\n\n <!-- Selected Apps Panel -->\n <div class=\"panel selected-apps-panel\" [class.collapsed]=\"SelectedPanelCollapsed\">\n <div class=\"panel-header\" (click)=\"SelectedPanelCollapsed = !SelectedPanelCollapsed\">\n <i class=\"fa-solid fa-check-circle\" aria-hidden=\"true\"></i>\n <h3>Your Applications</h3>\n <span class=\"badge\">{{ ActiveApps.length }}</span>\n <i\n class=\"collapse-toggle fa-solid\"\n [class.fa-chevron-down]=\"SelectedPanelCollapsed\"\n [class.fa-chevron-up]=\"!SelectedPanelCollapsed\"\n aria-hidden=\"true\"\n ></i>\n </div>\n\n <div class=\"panel-content\">\n @if (ActiveApps.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No applications selected\"\n Message=\"Add apps from the available list\" />\n }\n\n @if (ActiveApps.length > 0) {\n <div\n class=\"sortable-list\"\n (dragover)=\"OnDragOver($event)\"\n (drop)=\"OnDrop($event)\"\n >\n @for (item of ActiveApps; track item.app.ID; let i = $index) {\n <div\n class=\"app-item\"\n [class.dragging]=\"DraggedItem === item\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, item, i)\"\n (dragend)=\"OnDragEnd()\"\n (dragenter)=\"OnDragEnter($event, i)\"\n (dblclick)=\"RemoveApp(item); $event.stopPropagation()\"\n >\n <div class=\"app-item-content\" [style.border-left-color]=\"item.app.GetColor()\">\n <div class=\"drag-handle\">\n <i class=\"fa-solid fa-grip-vertical\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-info\">\n <span class=\"app-name\">{{ item.app.Name }}</span>\n @if (item.app.Description) {\n <span class=\"app-description\">{{ item.app.Description }}</span>\n }\n </div>\n\n <div class=\"app-actions\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n [disabled]=\"i === 0\"\n (click)=\"MoveUp(item); $event.stopPropagation()\"\n title=\"Move up\"\n [attr.aria-label]=\"'Move ' + item.app.Name + ' up'\"\n >\n <i class=\"fa-solid fa-chevron-up\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"flat\" size=\"sm\"\n [disabled]=\"i === ActiveApps.length - 1\"\n (click)=\"MoveDown(item); $event.stopPropagation()\"\n title=\"Move down\"\n [attr.aria-label]=\"'Move ' + item.app.Name + ' down'\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"RemoveApp(item); $event.stopPropagation()\"\n title=\"Remove from your apps\"\n [attr.aria-label]=\"'Remove ' + item.app.Name\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Mobile: single list with toggles + touch drag reorder -->\n <div\n class=\"mobile-view\"\n (touchmove)=\"OnTouchDragMove($event)\"\n (touchend)=\"OnTouchDragEnd()\"\n (touchcancel)=\"OnTouchDragEnd()\"\n >\n <div class=\"mobile-list\">\n @if (ActiveApps.length > 0) {\n <div class=\"mobile-section-label\">Your Apps</div>\n @for (item of ActiveApps; track item.app.ID; let i = $index) {\n @if (TouchDragActive && TouchDropIndex === i && TouchDragIndex > i) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n <div\n class=\"mobile-app-row\"\n [class.touch-ghost]=\"TouchDragActive && TouchDragIndex === i\"\n >\n <div\n class=\"mobile-drag-handle\"\n (touchstart)=\"OnTouchDragStart($event, i)\"\n aria-label=\"Drag to reorder\"\n >\n <i class=\"fa-solid fa-grip-vertical\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"mobile-app-name\">{{ item.app.Name }}</span>\n <button\n class=\"mobile-toggle on\"\n (click)=\"ToggleApp(item)\"\n aria-label=\"Remove {{ item.app.Name }}\"\n >\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n </button>\n </div>\n @if (TouchDragActive && TouchDropIndex === i && TouchDragIndex < i) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n }\n @if (TouchDragActive && TouchDropIndex === ActiveApps.length - 1 && TouchDragIndex < TouchDropIndex) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n }\n\n @if (AvailableApps.length > 0) {\n <div class=\"mobile-section-label\">Available</div>\n @for (item of AvailableApps; track item.app.ID) {\n <div class=\"mobile-app-row inactive\">\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"mobile-app-name\">{{ item.app.Name }}</span>\n <button\n class=\"mobile-toggle off\"\n (click)=\"ToggleApp(item)\"\n aria-label=\"Add {{ item.app.Name }}\"\n >\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Footer Actions -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n @if (HasChanges()) {\n <button\n mjButton\n (click)=\"Reset()\"\n [disabled]=\"IsLoading || IsSaving\"\n >\n <i class=\"fa-solid fa-rotate-left\" aria-hidden=\"true\"></i>\n Reset Changes\n </button>\n }\n </div>\n <div class=\"footer-right\">\n <button\n mjButton variant=\"primary\"\n (click)=\"Save()\"\n [disabled]=\"IsLoading || IsSaving || !HasChanges()\"\n >\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n <span>Saving...</span>\n } @else {\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span>Save Changes</span>\n }\n </button>\n <button\n mjButton\n (click)=\"Close()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"user-app-config.component.js","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config.component.ts","../../../src/lib/user-app-config/user-app-config.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ChangeDetectorRef, NgZone, ElementRef, inject } from '@angular/core';\nimport { Metadata, LogError, LogStatus, IMetadataProvider, LogStatusEx } from '@memberjunction/core';\nimport { MJUserApplicationEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { moveAndResequence, resequenceItems } from './user-app-config-reorder';\n/**\n * Represents an app item in the configuration UI\n */\ninterface AppConfigItem {\n app: BaseApplication;\n userAppId: string | null;\n sequence: number;\n isActive: boolean;\n isDirty: boolean;\n}\n\n/**\n * Full-screen modal dialog for configuring user's application visibility and order.\n * Allows users to:\n * - Select which applications to show in the app switcher\n * - Reorder applications via drag-and-drop (desktop)\n * - Toggle apps on/off in a single list (mobile)\n */\n@Component({\n standalone: false,\n selector: 'mj-user-app-config',\n templateUrl: './user-app-config.component.html',\n styleUrls: ['./user-app-config.component.css']\n})\nexport class UserAppConfigComponent extends BaseAngularComponent {\n private appManager = inject(ApplicationManager);\n private sharedService = inject(SharedService);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private el = inject(ElementRef);\n\n private _showDialog = false;\n\n @Input()\n set ShowDialog(value: boolean) {\n if (value !== this._showDialog) {\n this._showDialog = value;\n }\n }\n get ShowDialog(): boolean {\n return this._showDialog;\n }\n\n @Output() ShowDialogChange = new EventEmitter<boolean>();\n @Output() ConfigSaved = new EventEmitter<void>();\n\n AllApps: AppConfigItem[] = [];\n ActiveApps: AppConfigItem[] = [];\n AvailableApps: AppConfigItem[] = [];\n\n IsLoading = false;\n IsSaving = false;\n ErrorMessage = '';\n\n AvailablePanelCollapsed = false;\n SelectedPanelCollapsed = false;\n\n DraggedItem: AppConfigItem | null = null;\n DraggedIndex = -1;\n DropTargetIndex = -1;\n\n /**\n * Opens the dialog and loads user's app configuration\n */\n async Open(): Promise<void> {\n this._showDialog = true;\n this.ShowDialogChange.emit(true);\n this.ErrorMessage = '';\n await this.loadConfiguration();\n }\n\n /**\n * Closes the dialog without saving\n */\n Close(): void {\n this._showDialog = false;\n this.ShowDialogChange.emit(false);\n }\n\n /**\n * Adds an app to the user's active list\n */\n AddApp(item: AppConfigItem): void {\n item.isActive = true;\n item.sequence = this.ActiveApps.length;\n item.isDirty = true;\n this.refreshAppLists();\n }\n\n /**\n * Removes an app from the user's active list\n */\n RemoveApp(item: AppConfigItem): void {\n item.isActive = false;\n item.sequence = 999;\n item.isDirty = true;\n this.refreshAppLists();\n\n this.resequenceActiveApps();\n }\n\n /**\n * Toggles an app between active and inactive\n */\n ToggleApp(item: AppConfigItem): void {\n if (item.isActive) {\n this.RemoveApp(item);\n } else {\n this.AddApp(item);\n }\n }\n\n /**\n * Moves an app up in the order. Positional (splice + renumber), NOT a sequence-value\n * swap: rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled app\n * kept a stale value), and swapping two equal values is a silent no-op. Reordering by\n * position then renumbering 0..n-1 always works and heals duplicates as a side effect.\n */\n MoveUp(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index > 0) {\n this.moveActiveApp(index, index - 1);\n }\n }\n\n /**\n * Moves an app down in the order. Positional — see {@link MoveUp} for why.\n */\n MoveDown(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index >= 0 && index < this.ActiveApps.length - 1) {\n this.moveActiveApp(index, index + 1);\n }\n }\n\n private moveActiveApp(fromIndex: number, toIndex: number): void {\n this.ActiveApps = moveAndResequence(this.ActiveApps, fromIndex, toIndex);\n }\n\n /**\n * Checks if there are any unsaved changes\n */\n HasChanges(): boolean {\n return this.AllApps.some(item => item.isDirty);\n }\n\n /**\n * Saves the user's app configuration\n */\n async Save(): Promise<void> {\n if (!this.HasChanges()) {\n this.Close();\n return;\n }\n\n this.IsSaving = true;\n this.ErrorMessage = '';\n\n try {\n // Self-heal: renumber active apps 0..n-1 before persisting. Existing rows can\n // carry duplicate Sequence values from older data (issue #3027); renumbering in\n // display order marks any corrected rows dirty so this save fixes them for good.\n this.resequenceActiveApps();\n\n const md = this.ProviderToUse;\n\n for (const item of this.AllApps) {\n if (!item.isDirty) continue;\n\n if (item.userAppId) {\n await this.updateUserApplication(md, item);\n } else if (item.isActive) {\n await this.createUserApplication(md, item);\n }\n }\n\n // Each userApp.Save() above fires a BaseEntity 'save' event, which UserInfoEngine\n // catches via HandleIndividualBaseEntityEvent → debounced refresh → NotifyDataChange.\n // ApplicationManager.subscribeToEngineChanges then propagates to the shell app\n // switcher. No explicit reload needed here — and avoiding it sidesteps NG0100\n // from observable emissions interleaving with this method's save loop.\n LogStatusEx({\n message: 'User app configuration saved',\n verboseOnly: true \n });\n\n this.sharedService.CreateSimpleNotification('App configuration saved successfully!', 'success', 3000);\n this.ConfigSaved.emit();\n this.Close();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to save configuration. Please try again.';\n LogError('Error saving app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsSaving = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n /**\n * Resets all changes and reloads the configuration\n */\n async Reset(): Promise<void> {\n await this.loadConfiguration();\n }\n\n // ---------------------------------------------------------------------------\n // Drag-and-drop handlers (desktop only)\n // ---------------------------------------------------------------------------\n\n OnDragStart(event: DragEvent, item: AppConfigItem, index: number): void {\n this.DraggedItem = item;\n this.DraggedIndex = index;\n\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', index.toString());\n }\n }\n\n OnDragOver(event: DragEvent): void {\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move';\n }\n }\n\n OnDragEnter(event: DragEvent, index: number): void {\n event.preventDefault();\n this.DropTargetIndex = index;\n }\n\n OnDragEnd(): void {\n this.DraggedItem = null;\n this.DraggedIndex = -1;\n this.DropTargetIndex = -1;\n }\n\n OnDrop(event: DragEvent): void {\n event.preventDefault();\n\n if (this.DraggedIndex >= 0 && this.DropTargetIndex >= 0 && this.DraggedIndex !== this.DropTargetIndex) {\n const [movedItem] = this.ActiveApps.splice(this.DraggedIndex, 1);\n this.ActiveApps.splice(this.DropTargetIndex, 0, movedItem);\n this.resequenceActiveApps();\n this.cdr.detectChanges();\n }\n\n this.OnDragEnd();\n }\n\n // ---------------------------------------------------------------------------\n // Touch drag-and-drop handlers (mobile)\n // ---------------------------------------------------------------------------\n\n TouchDragIndex = -1;\n TouchDropIndex = -1;\n TouchDragActive = false;\n\n private touchStartY = 0;\n private touchCurrentY = 0;\n private touchRowHeight = 0;\n private touchDragElement: HTMLElement | null = null;\n private touchScrollContainer: HTMLElement | null = null;\n\n OnTouchDragStart(event: TouchEvent, index: number): void {\n const touch = event.touches[0];\n const row = (event.target as HTMLElement).closest('.mobile-app-row') as HTMLElement | null;\n if (!row) return;\n\n event.preventDefault();\n this.TouchDragIndex = index;\n this.TouchDropIndex = index;\n this.TouchDragActive = true;\n this.touchStartY = touch.clientY;\n this.touchCurrentY = touch.clientY;\n this.touchDragElement = row;\n this.touchRowHeight = row.offsetHeight;\n this.touchScrollContainer = this.el.nativeElement.querySelector('.mobile-list');\n\n row.classList.add('touch-dragging');\n }\n\n OnTouchDragMove(event: TouchEvent): void {\n if (!this.TouchDragActive || !this.touchDragElement) return;\n\n event.preventDefault();\n const touch = event.touches[0];\n this.touchCurrentY = touch.clientY;\n const deltaY = this.touchCurrentY - this.touchStartY;\n\n this.touchDragElement.style.transform = `translateY(${deltaY}px)`;\n this.touchDragElement.style.zIndex = '100';\n\n this.updateTouchDropTarget(deltaY);\n this.autoScrollIfNeeded();\n }\n\n OnTouchDragEnd(): void {\n if (!this.TouchDragActive) return;\n\n if (this.touchDragElement) {\n this.touchDragElement.style.transform = '';\n this.touchDragElement.style.zIndex = '';\n this.touchDragElement.classList.remove('touch-dragging');\n }\n\n if (this.TouchDragIndex >= 0 && this.TouchDropIndex >= 0 && this.TouchDragIndex !== this.TouchDropIndex) {\n const [movedItem] = this.ActiveApps.splice(this.TouchDragIndex, 1);\n this.ActiveApps.splice(this.TouchDropIndex, 0, movedItem);\n this.resequenceActiveApps();\n }\n\n this.resetTouchDragState();\n this.cdr.detectChanges();\n }\n\n private updateTouchDropTarget(deltaY: number): void {\n const rowsToMove = Math.round(deltaY / this.touchRowHeight);\n const newIndex = Math.max(0, Math.min(this.ActiveApps.length - 1, this.TouchDragIndex + rowsToMove));\n this.TouchDropIndex = newIndex;\n }\n\n private autoScrollIfNeeded(): void {\n if (!this.touchScrollContainer) return;\n\n const rect = this.touchScrollContainer.getBoundingClientRect();\n const edgeZone = 40;\n\n if (this.touchCurrentY < rect.top + edgeZone) {\n this.touchScrollContainer.scrollTop -= 8;\n } else if (this.touchCurrentY > rect.bottom - edgeZone) {\n this.touchScrollContainer.scrollTop += 8;\n }\n }\n\n private resetTouchDragState(): void {\n this.TouchDragIndex = -1;\n this.TouchDropIndex = -1;\n this.TouchDragActive = false;\n this.touchDragElement = null;\n this.touchScrollContainer = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async loadConfiguration(): Promise<void> {\n this.IsLoading = true;\n this.ErrorMessage = '';\n\n try {\n // Read directly from UserInfoEngine's cache. The engine already loaded\n // and maintains MJ: User Applications via its event-driven refresh — no\n // need to re-query the DB on every dialog open. EnsureLoaded is idempotent\n // and instant when the engine is already loaded (the typical case here,\n // since UserInfoEngine fires at startup).\n await UserInfoEngine.Instance.EnsureLoaded();\n const systemApps = this.appManager.GetAuthorizedSystemApps();\n const userApps = UserInfoEngine.Instance.UserApplications;\n this.AllApps = this.buildAppConfigItems(systemApps, userApps);\n this.refreshAppLists();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to load app configuration. Please try again.';\n LogError('Error loading app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsLoading = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n private buildAppConfigItems(systemApps: BaseApplication[], userApps: MJUserApplicationEntity[]): AppConfigItem[] {\n return systemApps.map(app => {\n const userApp = userApps.find(ua => UUIDsEqual(ua.ApplicationID, app.ID));\n return {\n app,\n userAppId: userApp?.ID ?? null,\n sequence: userApp?.Sequence ?? 999,\n isActive: userApp?.IsActive ?? false,\n isDirty: false\n };\n });\n }\n\n private refreshAppLists(): void {\n this.ActiveApps = this.AllApps\n .filter(item => item.isActive)\n .sort((a, b) => a.sequence - b.sequence);\n\n this.AvailableApps = this.AllApps\n .filter(item => !item.isActive)\n .sort((a, b) => a.app.Name.localeCompare(b.app.Name));\n }\n\n private resequenceActiveApps(): void {\n resequenceItems(this.ActiveApps);\n }\n\n\n private async updateUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n await userApp.Load(item.userAppId!);\n\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to update UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.isDirty = false;\n LogStatus(`Updated UserApplication for ${item.app.Name}: sequence=${item.sequence}, isActive=${item.isActive}`);\n }\n\n private async createUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n userApp.NewRecord();\n\n userApp.UserID = md.CurrentUser.ID;\n userApp.ApplicationID = item.app.ID;\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to create UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.userAppId = userApp.ID;\n item.isDirty = false;\n LogStatus(`Created UserApplication for ${item.app.Name}: sequence=${item.sequence}`);\n }\n}\n","<!-- Modal Backdrop -->\n@if (ShowDialog) {\n <div class=\"modal-backdrop\" (click)=\"Close()\" role=\"presentation\"></div>\n <div\n class=\"modal-dialog full-screen-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"app-config-title\"\n >\n <!-- Dialog Header -->\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" id=\"app-config-title\">\n <i class=\"fa-solid fa-grid-2\" aria-hidden=\"true\"></i>\n Configure Apps\n </h2>\n <button\n class=\"modal-close\"\n (click)=\"Close()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"app-config-container\">\n <!-- Loading State -->\n @if (IsLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\">\n <mj-loading text=\"Loading your app configuration...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Error Message -->\n @if (ErrorMessage) {\n <mj-alert Variant=\"error\">{{ ErrorMessage }}</mj-alert>\n }\n\n <!-- Main Content -->\n @if (!IsLoading) {\n <div class=\"config-content\">\n <p class=\"config-description\">\n Select which applications appear in your app switcher and arrange them in your preferred order.\n Double-click to quickly add or remove. Drag and drop to reorder, or use the arrow buttons.\n </p>\n\n <!-- Desktop: two-panel layout with drag-and-drop -->\n <div class=\"panels-container desktop-only\">\n <!-- Available Apps Panel -->\n <div class=\"panel available-apps-panel\" [class.collapsed]=\"AvailablePanelCollapsed\">\n <div class=\"panel-header\" (click)=\"AvailablePanelCollapsed = !AvailablePanelCollapsed\">\n <i class=\"fa-solid fa-layer-group\" aria-hidden=\"true\"></i>\n <h3>Available Applications</h3>\n <span class=\"badge\">{{ AvailableApps.length }}</span>\n <i\n class=\"collapse-toggle fa-solid\"\n [class.fa-chevron-down]=\"AvailablePanelCollapsed\"\n [class.fa-chevron-up]=\"!AvailablePanelCollapsed\"\n aria-hidden=\"true\"\n ></i>\n </div>\n\n <div class=\"panel-content\">\n @if (AvailableApps.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-check-double\"\n Title=\"All applications added!\" />\n }\n\n @for (item of AvailableApps; track item.app.ID) {\n <div class=\"available-app-item\" (dblclick)=\"AddApp(item)\">\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-info\">\n <span class=\"app-name\">{{ item.app.Name }}</span>\n @if (item.app.Description) {\n <span class=\"app-description\">{{ item.app.Description }}</span>\n }\n </div>\n\n <button\n mjButton variant=\"outline\" size=\"sm\"\n (click)=\"AddApp(item)\"\n title=\"Add to your apps\"\n [attr.aria-label]=\"'Add ' + item.app.Name\"\n >\n <i class=\"fa-solid fa-plus\" aria-hidden=\"true\"></i>\n Add\n </button>\n </div>\n }\n </div>\n </div>\n\n <!-- Selected Apps Panel -->\n <div class=\"panel selected-apps-panel\" [class.collapsed]=\"SelectedPanelCollapsed\">\n <div class=\"panel-header\" (click)=\"SelectedPanelCollapsed = !SelectedPanelCollapsed\">\n <i class=\"fa-solid fa-check-circle\" aria-hidden=\"true\"></i>\n <h3>Your Applications</h3>\n <span class=\"badge\">{{ ActiveApps.length }}</span>\n <i\n class=\"collapse-toggle fa-solid\"\n [class.fa-chevron-down]=\"SelectedPanelCollapsed\"\n [class.fa-chevron-up]=\"!SelectedPanelCollapsed\"\n aria-hidden=\"true\"\n ></i>\n </div>\n\n <div class=\"panel-content\">\n @if (ActiveApps.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No applications selected\"\n Message=\"Add apps from the available list\" />\n }\n\n @if (ActiveApps.length > 0) {\n <div\n class=\"sortable-list\"\n (dragover)=\"OnDragOver($event)\"\n (drop)=\"OnDrop($event)\"\n >\n @for (item of ActiveApps; track item.app.ID; let i = $index) {\n <div\n class=\"app-item\"\n [class.dragging]=\"DraggedItem === item\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, item, i)\"\n (dragend)=\"OnDragEnd()\"\n (dragenter)=\"OnDragEnter($event, i)\"\n (dblclick)=\"RemoveApp(item); $event.stopPropagation()\"\n >\n <div class=\"app-item-content\" [style.border-left-color]=\"item.app.GetColor()\">\n <div class=\"drag-handle\">\n <i class=\"fa-solid fa-grip-vertical\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-info\">\n <span class=\"app-name\">{{ item.app.Name }}</span>\n @if (item.app.Description) {\n <span class=\"app-description\">{{ item.app.Description }}</span>\n }\n </div>\n\n <div class=\"app-actions\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n [disabled]=\"i === 0\"\n (click)=\"MoveUp(item); $event.stopPropagation()\"\n title=\"Move up\"\n [attr.aria-label]=\"'Move ' + item.app.Name + ' up'\"\n >\n <i class=\"fa-solid fa-chevron-up\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"flat\" size=\"sm\"\n [disabled]=\"i === ActiveApps.length - 1\"\n (click)=\"MoveDown(item); $event.stopPropagation()\"\n title=\"Move down\"\n [attr.aria-label]=\"'Move ' + item.app.Name + ' down'\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"RemoveApp(item); $event.stopPropagation()\"\n title=\"Remove from your apps\"\n [attr.aria-label]=\"'Remove ' + item.app.Name\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Mobile: single list with toggles + touch drag reorder -->\n <div\n class=\"mobile-view\"\n (touchmove)=\"OnTouchDragMove($event)\"\n (touchend)=\"OnTouchDragEnd()\"\n (touchcancel)=\"OnTouchDragEnd()\"\n >\n <div class=\"mobile-list\">\n @if (ActiveApps.length > 0) {\n <div class=\"mobile-section-label\">Your Apps</div>\n @for (item of ActiveApps; track item.app.ID; let i = $index) {\n @if (TouchDragActive && TouchDropIndex === i && TouchDragIndex > i) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n <div\n class=\"mobile-app-row\"\n [class.touch-ghost]=\"TouchDragActive && TouchDragIndex === i\"\n >\n <div\n class=\"mobile-drag-handle\"\n (touchstart)=\"OnTouchDragStart($event, i)\"\n aria-label=\"Drag to reorder\"\n >\n <i class=\"fa-solid fa-grip-vertical\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"mobile-app-name\">{{ item.app.Name }}</span>\n <button\n class=\"mobile-toggle on\"\n (click)=\"ToggleApp(item)\"\n aria-label=\"Remove {{ item.app.Name }}\"\n >\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n </button>\n </div>\n @if (TouchDragActive && TouchDropIndex === i && TouchDragIndex < i) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n }\n @if (TouchDragActive && TouchDropIndex === ActiveApps.length - 1 && TouchDragIndex < TouchDropIndex) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n }\n\n @if (AvailableApps.length > 0) {\n <div class=\"mobile-section-label\">Available</div>\n @for (item of AvailableApps; track item.app.ID) {\n <div class=\"mobile-app-row inactive\">\n <div class=\"app-icon\" [style.background-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"mobile-app-name\">{{ item.app.Name }}</span>\n <button\n class=\"mobile-toggle off\"\n (click)=\"ToggleApp(item)\"\n aria-label=\"Add {{ item.app.Name }}\"\n >\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Footer Actions -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n @if (HasChanges()) {\n <button\n mjButton\n (click)=\"Reset()\"\n [disabled]=\"IsLoading || IsSaving\"\n >\n <i class=\"fa-solid fa-rotate-left\" aria-hidden=\"true\"></i>\n Reset Changes\n </button>\n }\n </div>\n <div class=\"footer-right\">\n <button\n mjButton variant=\"primary\"\n (click)=\"Save()\"\n [disabled]=\"IsLoading || IsSaving || !HasChanges()\"\n >\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n <span>Saving...</span>\n } @else {\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span>Save Changes</span>\n }\n </button>\n <button\n mjButton\n (click)=\"Close()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-explorer-settings",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.45.0",
|
|
4
4
|
"description": "MemberJunction: Reusable Angular components for the settings section of the MJ Explorer App",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -36,24 +36,24 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@angular/cdk": "21.1.3",
|
|
39
|
-
"@memberjunction/core": "5.
|
|
40
|
-
"@memberjunction/core-entities": "5.
|
|
41
|
-
"@memberjunction/global": "5.
|
|
42
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
43
|
-
"@memberjunction/ng-base-application": "5.
|
|
44
|
-
"@memberjunction/ng-base-forms": "5.
|
|
45
|
-
"@memberjunction/ng-base-types": "5.
|
|
46
|
-
"@memberjunction/ng-code-editor": "5.
|
|
47
|
-
"@memberjunction/ng-entity-form-dialog": "5.
|
|
48
|
-
"@memberjunction/ng-entity-permissions": "5.
|
|
49
|
-
"@memberjunction/ng-join-grid": "5.
|
|
50
|
-
"@memberjunction/ng-notifications": "5.
|
|
51
|
-
"@memberjunction/ng-shared": "5.
|
|
52
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
53
|
-
"@memberjunction/ng-simple-record-list": "5.
|
|
54
|
-
"@memberjunction/ng-tabstrip": "5.
|
|
55
|
-
"@memberjunction/ng-ui-components": "5.
|
|
56
|
-
"@memberjunction/ng-user-avatar": "5.
|
|
39
|
+
"@memberjunction/core": "5.45.0",
|
|
40
|
+
"@memberjunction/core-entities": "5.45.0",
|
|
41
|
+
"@memberjunction/global": "5.45.0",
|
|
42
|
+
"@memberjunction/graphql-dataprovider": "5.45.0",
|
|
43
|
+
"@memberjunction/ng-base-application": "5.45.0",
|
|
44
|
+
"@memberjunction/ng-base-forms": "5.45.0",
|
|
45
|
+
"@memberjunction/ng-base-types": "5.45.0",
|
|
46
|
+
"@memberjunction/ng-code-editor": "5.45.0",
|
|
47
|
+
"@memberjunction/ng-entity-form-dialog": "5.45.0",
|
|
48
|
+
"@memberjunction/ng-entity-permissions": "5.45.0",
|
|
49
|
+
"@memberjunction/ng-join-grid": "5.45.0",
|
|
50
|
+
"@memberjunction/ng-notifications": "5.45.0",
|
|
51
|
+
"@memberjunction/ng-shared": "5.45.0",
|
|
52
|
+
"@memberjunction/ng-shared-generic": "5.45.0",
|
|
53
|
+
"@memberjunction/ng-simple-record-list": "5.45.0",
|
|
54
|
+
"@memberjunction/ng-tabstrip": "5.45.0",
|
|
55
|
+
"@memberjunction/ng-ui-components": "5.45.0",
|
|
56
|
+
"@memberjunction/ng-user-avatar": "5.45.0",
|
|
57
57
|
"rxjs": "~7.8.2",
|
|
58
58
|
"tslib": "^2.8.1"
|
|
59
59
|
},
|