codeapp-js 0.2.2 → 1.0.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/AI/codeapp.agent.md +105 -0
- package/AI/skills/connections/SKILL.md +47 -0
- package/AI/skills/dataverse/SKILL.md +99 -0
- package/AI/skills/environment-variables/SKILL.md +89 -0
- package/AI/skills/frontend-design/SKILL.md +34 -0
- package/AI/skills/jira/SKILL.md +81 -0
- package/AI/skills/office365-groups/SKILL.md +61 -0
- package/AI/skills/office365-outlook/SKILL.md +52 -0
- package/AI/skills/office365-users/SKILL.md +78 -0
- package/AI/skills/sharepoint/SKILL.md +77 -0
- package/AI/skills/sql/SKILL.md +85 -0
- package/AI/skills/start/SKILL.md +46 -0
- package/AI/skills/teams/SKILL.md +55 -0
- package/codeApp/.power/schemas/appschemas/dataSourcesInfo.ts +6275 -0
- package/codeApp/.power/schemas/jira/jira.Schema.json +6903 -0
- package/codeApp/.power/schemas/keyvault/keyvault.Schema.json +1600 -0
- package/{examples/combined demo/.power/schemas/office365groups/office365groups.Schema.json → codeApp/.power/schemas/office365groups/office365groups.Schema.json} +2203 -2203
- package/codeApp/.power/schemas/teams/teams.Schema.json +11112 -0
- package/codeApp/dist/codeapp.js +103 -1043
- package/codeApp/dist/connectors/azureKeyvault.js +459 -0
- package/codeApp/dist/connectors/jira.js +1247 -0
- package/codeApp/dist/connectors/office365groups.js +642 -0
- package/codeApp/dist/connectors/office365users.js +513 -0
- package/codeApp/dist/connectors/outlook.js +1393 -0
- package/{examples/kanban/dist → codeApp/dist/connectors}/sharepoint.js +466 -339
- package/codeApp/dist/connectors/sql.js +149 -0
- package/codeApp/dist/connectors/teams.js +280 -0
- package/codeApp/dist/index.js +1 -1
- package/codeApp/dist/power-apps-data.js +725 -176
- package/codeApp/src/generated/index.ts +12 -0
- package/codeApp/src/generated/models/AzureKeyVaultModel.ts +107 -0
- package/codeApp/src/generated/models/JiraModel.ts +501 -0
- package/codeApp/src/generated/services/AzureKeyVaultService.ts +257 -0
- package/codeApp/src/generated/services/JiraService.ts +1124 -0
- package/examples/{kanban → apps/kanban}/dist/dataverse.js +94 -94
- package/examples/{kanban → apps/kanban}/dist/index.css +605 -605
- package/examples/{kanban → apps/kanban}/dist/index.html +21 -21
- package/examples/{kanban → apps/kanban}/dist/index.js +860 -860
- package/examples/{kanban → apps/kanban}/dist/office365groups.js +97 -97
- package/examples/apps/kanban/dist/office365users.js +451 -0
- package/examples/{kanban → apps/kanban}/dist/outlook.js +162 -162
- package/examples/{planning Poker/dist/power-apps-data.js → apps/kanban/dist/power-apps-data.js} +2953 -2953
- package/{dev files/sharepoint.js → examples/apps/kanban/dist/sharepoint.js} +195 -99
- package/examples/{kanban → apps/kanban}/power.config.json +35 -35
- package/examples/{planning Poker → apps/planning Poker}/additional files/customizations (tables).xml +6428 -6428
- package/examples/{planning Poker → apps/planning Poker}/additional files/dataverse-tables.json +165 -165
- package/examples/{planning Poker → apps/planning Poker}/additional files/readme.md +122 -122
- package/examples/{planning Poker → apps/planning Poker}/dist/dataverse.js +78 -78
- package/examples/{planning Poker → apps/planning Poker}/dist/index.html +198 -198
- package/examples/{planning Poker → apps/planning Poker}/dist/index.js +954 -954
- package/examples/{kanban/dist/power-apps-data.js → apps/planning Poker/dist/power-apps-data.js } +2953 -2953
- package/examples/{planning Poker → apps/planning Poker}/dist/styles.css +815 -815
- package/examples/{planning Poker → apps/planning Poker}/power.config.json +50 -50
- package/examples/{solution explorer → apps/solution explorer}/dist/codeapp.js +1098 -1098
- package/examples/{solution explorer → apps/solution explorer}/dist/index.html +80 -80
- package/examples/{solution explorer → apps/solution explorer}/dist/index.js +735 -735
- package/examples/{solution explorer → apps/solution explorer}/dist/styles.css +571 -571
- package/examples/{solution explorer → apps/solution explorer}/power.config.json +150 -150
- package/examples/{todo → apps/todo}/dist/dataverse.js +64 -64
- package/examples/{todo → apps/todo}/dist/index.html +75 -75
- package/examples/{todo → apps/todo}/dist/index.js +8 -8
- package/examples/{todo → apps/todo}/dist/power-apps-data.js +2953 -2953
- package/examples/{todo → apps/todo}/dist/renderer.js +375 -375
- package/examples/{todo → apps/todo}/dist/styles.css +691 -691
- package/examples/{todo → apps/todo}/power.config.json +34 -34
- package/examples/combined demo/.power/schemas/appschemas/dataSourcesInfo.ts +6275 -7830
- package/examples/combined demo/.power/schemas/jira/jira.Schema.json +6903 -0
- package/examples/combined demo/.power/schemas/keyvault/keyvault.Schema.json +1600 -0
- package/examples/combined demo/.power/schemas/teams/teams.Schema.json +11112 -0
- package/examples/combined demo/dist/codeapp.js +394 -1098
- package/examples/combined demo/dist/index.html +29 -511
- package/examples/combined demo/dist/index.js +490 -470
- package/examples/combined demo/dist/office365users.js +513 -0
- package/examples/combined demo/dist/outlook.js +1393 -0
- package/examples/combined demo/dist/power-apps-data.js +3079 -3006
- package/examples/combined demo/dist/styles.css +483 -0
- package/examples/combined demo/power.config.json +33 -42
- package/examples/combined demo/src/generated/index.ts +12 -14
- package/examples/combined demo/src/generated/models/AzureKeyVaultModel.ts +107 -0
- package/examples/combined demo/src/generated/models/JiraModel.ts +501 -0
- package/examples/combined demo/src/generated/services/AzureKeyVaultService.ts +257 -0
- package/examples/combined demo/src/generated/services/JiraService.ts +1124 -0
- package/examples/dataverse Demo/dist/codeapp.js +394 -1085
- package/examples/{outlook Demo2/OutlookDemo_1_0_0_1.zip → dataverse Demo/dist/icon-512.png} +0 -0
- package/examples/dataverse Demo/dist/index.html +146 -54
- package/examples/dataverse Demo/dist/index.js +693 -83
- package/examples/dataverse Demo/dist/power-apps-data.js +3079 -2911
- package/examples/dataverse Demo/dist/styles.css +528 -0
- package/examples/dataverse Demo/power.config.json +41 -35
- package/examples/dataverse Demo/readme.md +79 -79
- package/examples/groups Demo/dist/codeapp.js +394 -1085
- package/examples/groups Demo/dist/icon-512.png +0 -0
- package/examples/groups Demo/dist/index.html +21 -25
- package/examples/groups Demo/dist/index.js +304 -113
- package/examples/groups Demo/dist/office365groups.js +642 -0
- package/examples/groups Demo/dist/power-apps-data.js +3079 -2911
- package/examples/groups Demo/dist/styles.css +509 -0
- package/examples/groups Demo/power.config.json +25 -25
- package/examples/myProfile/dist/codeapp.js +398 -0
- package/examples/myProfile/dist/index.html +21 -184
- package/examples/myProfile/dist/index.js +324 -141
- package/examples/myProfile/dist/office365users.js +517 -169
- package/examples/myProfile/dist/power-apps-data.js +3080 -2953
- package/examples/myProfile/dist/styles.css +458 -0
- package/examples/myProfile/power.config.json +24 -23
- package/examples/outlook Demo/dist/codeapp.js +394 -1085
- package/examples/outlook Demo/dist/index.html +150 -35
- package/examples/outlook Demo/dist/index.js +516 -170
- package/examples/outlook Demo/dist/outlook.js +1393 -121
- package/examples/outlook Demo/dist/power-apps-data.js +3079 -2911
- package/examples/outlook Demo/dist/styles.css +408 -84
- package/examples/outlook Demo/power.config.json +24 -23
- package/examples/outlook Demo/readme.md +92 -82
- package/examples/sharePoint Demo/dist/codeapp.js +394 -1085
- package/examples/sharePoint Demo/dist/icon-512.png +0 -0
- package/examples/sharePoint Demo/dist/index.html +22 -255
- package/examples/sharePoint Demo/dist/index.js +899 -262
- package/examples/sharePoint Demo/dist/power-apps-data.js +3079 -2911
- package/examples/sharePoint Demo/dist/sharepoint.js +466 -0
- package/examples/sharePoint Demo/dist/styles.css +587 -0
- package/examples/sharePoint Demo/power.config.json +23 -22
- package/package.json +1 -1
- package/readme.md +479 -61
- package/.github/instructions/wyattdave.instructions.md +0 -39
- package/.vscode/settings.json +0 -6
- package/dev files/dataverse.js +0 -105
- package/dev files/office365groups.js +0 -65
- package/dev files/office365users.js +0 -169
- package/dev files/outlook.js +0 -330
- package/examples/combined demo/.power/schemas/office365/office365.Schema.json +0 -21098
- package/examples/combined demo/.power/schemas/office365users/office365users.Schema.json +0 -2094
- package/examples/kanban/agent/decision-log.md +0 -9
- package/examples/kanban/agent/mockup-01-editorial-glass.html +0 -159
- package/examples/kanban/agent/mockup-02-dark-rail.html +0 -147
- package/examples/kanban/agent/mockup-03-paper-grid.html +0 -114
- package/examples/kanban/agent/mockup-04-neon-minimal.html +0 -141
- package/examples/kanban/agent/mockup-05-mono-architect.html +0 -119
- package/examples/kanban/dist/environmentVar.js +0 -55
- package/examples/kanban/dist/office365users.js +0 -169
- package/examples/kanban/src/generated/index.ts +0 -14
- package/examples/outlook Demo2/agent/decision-log.md +0 -7
- package/examples/outlook Demo2/dist/codeapp.js +0 -1334
- package/examples/outlook Demo2/dist/index.html +0 -98
- package/examples/outlook Demo2/dist/index.js +0 -346
- package/examples/outlook Demo2/dist/styles.css +0 -639
- package/examples/outlook Demo2/power.config.json +0 -23
- package/examples/planning Poker/.vscode/settings.json +0 -5
- package/examples/sharePoint Demo/agent/decision-log.md +0 -17
- package/examples/solution explorer/agent/decision-log.md +0 -27
- package/examples/solution explorer/agent/mockup-01-swiss-grid.html +0 -452
- package/examples/solution explorer/agent/mockup-02-dark-glass.html +0 -496
- package/examples/solution explorer/agent/mockup-03-paper-console.html +0 -510
- package/examples/solution explorer/agent/mockup-04-neon-noir.html +0 -546
- package/examples/solution explorer/agent/mockup-05-zen-garden.html +0 -534
- package/examples/solution explorer/dist/power-apps-data.js +0 -3007
- package/scripts/build-power-sdk.mjs +0 -69
- /package/{examples/kanban → codeApp}/src/generated/models/Office365GroupsModel.ts +0 -0
- /package/{examples/kanban → codeApp}/src/generated/models/Office365OutlookModel.ts +0 -0
- /package/{examples/kanban → codeApp}/src/generated/models/Office365UsersModel.ts +0 -0
- /package/{examples/kanban → codeApp}/src/generated/services/Office365GroupsService.ts +0 -0
- /package/{examples/kanban → codeApp}/src/generated/services/Office365OutlookService.ts +0 -0
- /package/{examples/kanban → codeApp}/src/generated/services/Office365UsersService.ts +0 -0
- /package/{dev files → examples/apps/kanban/dist}/environmentVar.js +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/index.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/models/Office365GroupsModel.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/models/Office365OutlookModel.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/models/Office365UsersModel.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/services/Office365GroupsService.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/services/Office365OutlookService.ts +0 -0
- /package/examples/{outlook Demo2 → apps/kanban}/src/generated/services/Office365UsersService.ts +0 -0
- /package/examples/{planning Poker → apps/planning Poker}/additional files/AgilePoker_1_0_0_1.zip +0 -0
- /package/examples/{planning Poker → apps/planning Poker}/additional files/PokerTables_1_0_0_1.zip +0 -0
- /package/examples/{outlook Demo2 → apps/solution explorer}/dist/icon-512.png +0 -0
- /package/examples/{outlook Demo2 → apps/solution explorer}/dist/power-apps-data.js +0 -0
- /package/examples/{todo → apps/todo}/dist/icon192.png +0 -0
- /package/examples/{solution explorer → combined demo}/dist/icon-512.png +0 -0
|
@@ -1,83 +1,693 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
import { enableDebugger } from "./codeapp.js";
|
|
2
|
+
|
|
3
|
+
enableDebugger();
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
initDataSources,
|
|
7
|
+
listItems,
|
|
8
|
+
createItem,
|
|
9
|
+
updateItem,
|
|
10
|
+
deleteItem,
|
|
11
|
+
callUnboundAction,
|
|
12
|
+
whoAmI,
|
|
13
|
+
} from './codeapp.js';
|
|
14
|
+
|
|
15
|
+
const sTaskTableName = 'tasks';
|
|
16
|
+
const sTaskPrimaryKey = 'activityid';
|
|
17
|
+
const sUserTableName = 'systemusers';
|
|
18
|
+
const sUserPrimaryKey = 'systemuserid';
|
|
19
|
+
|
|
20
|
+
const aAccessLevels = [
|
|
21
|
+
{
|
|
22
|
+
id: 'read',
|
|
23
|
+
label: 'Read only',
|
|
24
|
+
mask: 'ReadAccess',
|
|
25
|
+
note: 'View the task record.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'work',
|
|
29
|
+
label: 'Collaborate',
|
|
30
|
+
mask: 'ReadAccess,WriteAccess,AppendAccess,AppendToAccess',
|
|
31
|
+
note: 'Update the task and related notes.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'share',
|
|
35
|
+
label: 'Collaborate + reshare',
|
|
36
|
+
mask: 'ReadAccess,WriteAccess,AppendAccess,AppendToAccess,ShareAccess',
|
|
37
|
+
note: 'Update the task and share it onward.',
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const oState = {
|
|
42
|
+
aTasks: [],
|
|
43
|
+
aUsers: [],
|
|
44
|
+
aShareEvents: [],
|
|
45
|
+
sSelectedTaskId: '',
|
|
46
|
+
sCurrentUserId: '',
|
|
47
|
+
sStatusText: 'Booting Dataverse task workspace...',
|
|
48
|
+
sStatusTone: 'live',
|
|
49
|
+
sShareStatusText: 'Select a task, choose a user, then grant access.',
|
|
50
|
+
sListCaption: 'Showing the latest task records.',
|
|
51
|
+
bLoading: false,
|
|
52
|
+
bSaving: false,
|
|
53
|
+
bSharing: false,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const oElements = {
|
|
57
|
+
eConnectionState: document.getElementById('connectionState'),
|
|
58
|
+
eTaskCount: document.getElementById('taskCount'),
|
|
59
|
+
eUserCount: document.getElementById('userCount'),
|
|
60
|
+
eTaskSearch: document.getElementById('taskSearch'),
|
|
61
|
+
ePriorityFilter: document.getElementById('priorityFilter'),
|
|
62
|
+
eStatusTone: document.getElementById('statusTone'),
|
|
63
|
+
eStatusText: document.getElementById('statusText'),
|
|
64
|
+
eCurrentUserText: document.getElementById('currentUserText'),
|
|
65
|
+
eTaskList: document.getElementById('taskList'),
|
|
66
|
+
eListCaption: document.getElementById('listCaption'),
|
|
67
|
+
eEditorHeading: document.getElementById('editorHeading'),
|
|
68
|
+
eRecordMeta: document.getElementById('recordMeta'),
|
|
69
|
+
eTaskForm: document.getElementById('taskForm'),
|
|
70
|
+
eTaskTitle: document.getElementById('taskTitle'),
|
|
71
|
+
eTaskPriority: document.getElementById('taskPriority'),
|
|
72
|
+
eTaskDueDate: document.getElementById('taskDueDate'),
|
|
73
|
+
eTaskDescription: document.getElementById('taskDescription'),
|
|
74
|
+
eBtnRefresh: document.getElementById('btnRefresh'),
|
|
75
|
+
eBtnNewTask: document.getElementById('btnNewTask'),
|
|
76
|
+
eBtnResetTask: document.getElementById('btnResetTask'),
|
|
77
|
+
eBtnDeleteTask: document.getElementById('btnDeleteTask'),
|
|
78
|
+
eShareUser: document.getElementById('shareUser'),
|
|
79
|
+
eShareAccess: document.getElementById('shareAccess'),
|
|
80
|
+
eBtnShareTask: document.getElementById('btnShareTask'),
|
|
81
|
+
eShareStatusText: document.getElementById('shareStatusText'),
|
|
82
|
+
eShareLog: document.getElementById('shareLog'),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function dsEntry(sPrimaryKey) {
|
|
86
|
+
return {
|
|
87
|
+
tableId: '',
|
|
88
|
+
version: '',
|
|
89
|
+
primaryKey: sPrimaryKey,
|
|
90
|
+
dataSourceType: 'Dataverse',
|
|
91
|
+
apis: {},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function normalizeGuid(sValue) {
|
|
96
|
+
return String(sValue || '').split('{').join('').split('}').join('').toLowerCase();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function normalizeDateValue(sValue) {
|
|
100
|
+
if (!sValue) {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return String(sValue).slice(0, 10);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatDateLabel(sValue) {
|
|
108
|
+
if (!sValue) {
|
|
109
|
+
return 'No due date';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const oDate = new Date(sValue);
|
|
113
|
+
if (Number.isNaN(oDate.getTime())) {
|
|
114
|
+
return 'No due date';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return oDate.toLocaleDateString(undefined, {
|
|
118
|
+
month: 'short',
|
|
119
|
+
day: 'numeric',
|
|
120
|
+
year: 'numeric',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatDateTimeLabel(sValue) {
|
|
125
|
+
if (!sValue) {
|
|
126
|
+
return 'Not yet saved';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const oDate = new Date(sValue);
|
|
130
|
+
if (Number.isNaN(oDate.getTime())) {
|
|
131
|
+
return 'Not yet saved';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return oDate.toLocaleString(undefined, {
|
|
135
|
+
month: 'short',
|
|
136
|
+
day: 'numeric',
|
|
137
|
+
hour: 'numeric',
|
|
138
|
+
minute: '2-digit',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function priorityLabelFromCode(iCode) {
|
|
143
|
+
if (Number(iCode) === 2) {
|
|
144
|
+
return 'High';
|
|
145
|
+
}
|
|
146
|
+
if (Number(iCode) === 0) {
|
|
147
|
+
return 'Low';
|
|
148
|
+
}
|
|
149
|
+
return 'Normal';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function stateLabelFromCode(iCode) {
|
|
153
|
+
if (Number(iCode) === 1) {
|
|
154
|
+
return 'Completed';
|
|
155
|
+
}
|
|
156
|
+
if (Number(iCode) === 2) {
|
|
157
|
+
return 'Canceled';
|
|
158
|
+
}
|
|
159
|
+
return 'Open';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function setStatus(sText, sTone) {
|
|
163
|
+
oState.sStatusText = sText;
|
|
164
|
+
oState.sStatusTone = sTone || 'live';
|
|
165
|
+
renderStatus();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function setShareStatus(sText, sTone) {
|
|
169
|
+
oState.sShareStatusText = sText;
|
|
170
|
+
oElements.eShareStatusText.textContent = sText;
|
|
171
|
+
oElements.eShareStatusText.dataset.tone = sTone || 'live';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getSelectedTask() {
|
|
175
|
+
return oState.aTasks.find((oTask) => oTask.id === oState.sSelectedTaskId) || null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getVisibleTasks() {
|
|
179
|
+
const sSearch = String(oElements.eTaskSearch.value || '').trim().toLowerCase();
|
|
180
|
+
const sPriorityFilter = String(oElements.ePriorityFilter.value || 'all');
|
|
181
|
+
|
|
182
|
+
return oState.aTasks.filter((oTask) => {
|
|
183
|
+
const bMatchesSearch = !sSearch
|
|
184
|
+
|| String(oTask.subject || '').toLowerCase().includes(sSearch)
|
|
185
|
+
|| String(oTask.description || '').toLowerCase().includes(sSearch);
|
|
186
|
+
const bMatchesPriority = sPriorityFilter === 'all' || String(oTask.prioritycode) === sPriorityFilter;
|
|
187
|
+
return bMatchesSearch && bMatchesPriority;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function mapTaskRecord(oRecord) {
|
|
192
|
+
return {
|
|
193
|
+
id: oRecord.activityid || oRecord.taskid || '',
|
|
194
|
+
subject: oRecord.subject || '',
|
|
195
|
+
description: oRecord.description || '',
|
|
196
|
+
prioritycode: Number(oRecord.prioritycode),
|
|
197
|
+
scheduledend: normalizeDateValue(oRecord.scheduledend),
|
|
198
|
+
statecode: Number(oRecord.statecode),
|
|
199
|
+
statuscode: Number(oRecord.statuscode),
|
|
200
|
+
modifiedon: oRecord.modifiedon || '',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function mapUserRecord(oRecord) {
|
|
205
|
+
return {
|
|
206
|
+
id: oRecord.systemuserid || '',
|
|
207
|
+
fullname: oRecord.fullname || 'Unnamed user',
|
|
208
|
+
email: oRecord.internalemailaddress || oRecord.domainname || '',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function renderStatus() {
|
|
213
|
+
oElements.eConnectionState.textContent = oState.bLoading ? 'Refreshing' : 'Connected';
|
|
214
|
+
if (oState.sStatusTone === 'error') {
|
|
215
|
+
oElements.eConnectionState.textContent = 'Attention';
|
|
216
|
+
}
|
|
217
|
+
oElements.eStatusTone.textContent = oState.sStatusTone;
|
|
218
|
+
oElements.eStatusTone.dataset.tone = oState.sStatusTone;
|
|
219
|
+
oElements.eStatusText.textContent = oState.sStatusText;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function renderMetrics() {
|
|
223
|
+
oElements.eTaskCount.textContent = String(oState.aTasks.length);
|
|
224
|
+
oElements.eUserCount.textContent = String(oState.aUsers.length);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function renderTaskList() {
|
|
228
|
+
const aVisibleTasks = getVisibleTasks();
|
|
229
|
+
oElements.eTaskList.innerHTML = '';
|
|
230
|
+
|
|
231
|
+
if (aVisibleTasks.length === 0) {
|
|
232
|
+
const eEmpty = document.createElement('div');
|
|
233
|
+
eEmpty.className = 'empty-card';
|
|
234
|
+
eEmpty.textContent = 'No Dataverse task records match the current filters.';
|
|
235
|
+
oElements.eTaskList.appendChild(eEmpty);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
aVisibleTasks.forEach((oTask) => {
|
|
239
|
+
const eButton = document.createElement('button');
|
|
240
|
+
eButton.type = 'button';
|
|
241
|
+
eButton.className = 'task-card';
|
|
242
|
+
if (oTask.id === oState.sSelectedTaskId) {
|
|
243
|
+
eButton.className += ' task-card--active';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const eTopRow = document.createElement('div');
|
|
247
|
+
eTopRow.className = 'task-card__row';
|
|
248
|
+
|
|
249
|
+
const eTitle = document.createElement('h3');
|
|
250
|
+
eTitle.className = 'task-card__title';
|
|
251
|
+
eTitle.textContent = oTask.subject || 'Untitled task';
|
|
252
|
+
|
|
253
|
+
const ePriority = document.createElement('span');
|
|
254
|
+
ePriority.className = 'pill pill--priority';
|
|
255
|
+
ePriority.textContent = priorityLabelFromCode(oTask.prioritycode);
|
|
256
|
+
|
|
257
|
+
eTopRow.appendChild(eTitle);
|
|
258
|
+
eTopRow.appendChild(ePriority);
|
|
259
|
+
|
|
260
|
+
const eDescription = document.createElement('p');
|
|
261
|
+
eDescription.className = 'task-card__description';
|
|
262
|
+
eDescription.textContent = oTask.description || 'No description';
|
|
263
|
+
|
|
264
|
+
const eBottomRow = document.createElement('div');
|
|
265
|
+
eBottomRow.className = 'task-card__row task-card__row--meta';
|
|
266
|
+
|
|
267
|
+
const eDue = document.createElement('span');
|
|
268
|
+
eDue.className = 'pill';
|
|
269
|
+
eDue.textContent = formatDateLabel(oTask.scheduledend);
|
|
270
|
+
|
|
271
|
+
const eState = document.createElement('span');
|
|
272
|
+
eState.className = 'pill';
|
|
273
|
+
eState.textContent = stateLabelFromCode(oTask.statecode);
|
|
274
|
+
|
|
275
|
+
eBottomRow.appendChild(eDue);
|
|
276
|
+
eBottomRow.appendChild(eState);
|
|
277
|
+
|
|
278
|
+
eButton.appendChild(eTopRow);
|
|
279
|
+
eButton.appendChild(eDescription);
|
|
280
|
+
eButton.appendChild(eBottomRow);
|
|
281
|
+
|
|
282
|
+
eButton.addEventListener('click', () => {
|
|
283
|
+
selectTask(oTask.id);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
oElements.eTaskList.appendChild(eButton);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
oState.sListCaption = aVisibleTasks.length + ' task' + (aVisibleTasks.length === 1 ? '' : 's') + ' shown from Dataverse.';
|
|
290
|
+
oElements.eListCaption.textContent = oState.sListCaption;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function renderShareOptions() {
|
|
294
|
+
const sSelectedAccessId = String(oElements.eShareAccess.value || 'read');
|
|
295
|
+
const sSelectedUserId = String(oElements.eShareUser.value || '');
|
|
296
|
+
|
|
297
|
+
oElements.eShareAccess.innerHTML = '';
|
|
298
|
+
aAccessLevels.forEach((oLevel) => {
|
|
299
|
+
const eOption = document.createElement('option');
|
|
300
|
+
eOption.value = oLevel.id;
|
|
301
|
+
eOption.textContent = oLevel.label + ' - ' + oLevel.note;
|
|
302
|
+
oElements.eShareAccess.appendChild(eOption);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (aAccessLevels.some((oLevel) => oLevel.id === sSelectedAccessId)) {
|
|
306
|
+
oElements.eShareAccess.value = sSelectedAccessId;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
oElements.eShareUser.innerHTML = '';
|
|
310
|
+
if (oState.aUsers.length === 0) {
|
|
311
|
+
const eOption = document.createElement('option');
|
|
312
|
+
eOption.value = '';
|
|
313
|
+
eOption.textContent = 'No Dataverse users loaded';
|
|
314
|
+
oElements.eShareUser.appendChild(eOption);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const ePlaceholder = document.createElement('option');
|
|
319
|
+
ePlaceholder.value = '';
|
|
320
|
+
ePlaceholder.textContent = 'Select a Dataverse user';
|
|
321
|
+
oElements.eShareUser.appendChild(ePlaceholder);
|
|
322
|
+
|
|
323
|
+
oState.aUsers.forEach((oUser) => {
|
|
324
|
+
const eOption = document.createElement('option');
|
|
325
|
+
eOption.value = oUser.id;
|
|
326
|
+
eOption.textContent = oUser.fullname + (oUser.email ? ' - ' + oUser.email : '');
|
|
327
|
+
oElements.eShareUser.appendChild(eOption);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (oState.aUsers.some((oUser) => oUser.id === sSelectedUserId)) {
|
|
331
|
+
oElements.eShareUser.value = sSelectedUserId;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function renderShareLog() {
|
|
336
|
+
oElements.eShareLog.innerHTML = '';
|
|
337
|
+
if (oState.aShareEvents.length === 0) {
|
|
338
|
+
const eEmpty = document.createElement('div');
|
|
339
|
+
eEmpty.className = 'empty-card empty-card--compact';
|
|
340
|
+
eEmpty.textContent = 'Share events from this session will appear here.';
|
|
341
|
+
oElements.eShareLog.appendChild(eEmpty);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
oState.aShareEvents.forEach((oEvent) => {
|
|
346
|
+
const eItem = document.createElement('article');
|
|
347
|
+
eItem.className = 'share-log__item';
|
|
348
|
+
|
|
349
|
+
const eHeadline = document.createElement('strong');
|
|
350
|
+
eHeadline.textContent = oEvent.title;
|
|
351
|
+
|
|
352
|
+
const eMeta = document.createElement('p');
|
|
353
|
+
eMeta.textContent = oEvent.detail;
|
|
354
|
+
|
|
355
|
+
eItem.appendChild(eHeadline);
|
|
356
|
+
eItem.appendChild(eMeta);
|
|
357
|
+
oElements.eShareLog.appendChild(eItem);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function renderEditor() {
|
|
362
|
+
const oTask = getSelectedTask();
|
|
363
|
+
if (!oTask) {
|
|
364
|
+
oElements.eEditorHeading.textContent = 'New task';
|
|
365
|
+
oElements.eRecordMeta.textContent = 'Create a new Dataverse task record.';
|
|
366
|
+
oElements.eBtnDeleteTask.disabled = true;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
oElements.eEditorHeading.textContent = oTask.subject || 'Selected task';
|
|
371
|
+
oElements.eRecordMeta.textContent = 'Last updated ' + formatDateTimeLabel(oTask.modifiedon) + ' - ' + oTask.id;
|
|
372
|
+
oElements.eBtnDeleteTask.disabled = false;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function renderCurrentUser() {
|
|
376
|
+
if (!oState.sCurrentUserId) {
|
|
377
|
+
oElements.eCurrentUserText.textContent = 'Current Dataverse user could not be detected.';
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
oElements.eCurrentUserText.textContent = 'Current Dataverse user: ' + oState.sCurrentUserId;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function renderActionState() {
|
|
385
|
+
const bHasSelectedTask = !!getSelectedTask();
|
|
386
|
+
const bHasShareTarget = !!oElements.eShareUser.value;
|
|
387
|
+
const bDisableWrite = oState.bSaving || oState.bLoading;
|
|
388
|
+
|
|
389
|
+
oElements.eBtnRefresh.disabled = oState.bLoading;
|
|
390
|
+
oElements.eBtnNewTask.disabled = bDisableWrite;
|
|
391
|
+
oElements.eBtnResetTask.disabled = bDisableWrite;
|
|
392
|
+
oElements.eBtnDeleteTask.disabled = !bHasSelectedTask || bDisableWrite;
|
|
393
|
+
oElements.eBtnShareTask.disabled = !bHasSelectedTask || !bHasShareTarget || oState.bSharing || oState.bLoading;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function renderAll() {
|
|
397
|
+
renderStatus();
|
|
398
|
+
renderMetrics();
|
|
399
|
+
renderTaskList();
|
|
400
|
+
renderEditor();
|
|
401
|
+
renderShareOptions();
|
|
402
|
+
renderShareLog();
|
|
403
|
+
renderCurrentUser();
|
|
404
|
+
renderActionState();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function fillForm(oTask) {
|
|
408
|
+
oElements.eTaskTitle.value = oTask ? oTask.subject : '';
|
|
409
|
+
oElements.eTaskPriority.value = oTask ? String(oTask.prioritycode) : '1';
|
|
410
|
+
oElements.eTaskDueDate.value = oTask ? normalizeDateValue(oTask.scheduledend) : '';
|
|
411
|
+
oElements.eTaskDescription.value = oTask ? oTask.description : '';
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function selectTask(sTaskId) {
|
|
415
|
+
oState.sSelectedTaskId = sTaskId || '';
|
|
416
|
+
fillForm(getSelectedTask());
|
|
417
|
+
if (oState.sSelectedTaskId) {
|
|
418
|
+
setShareStatus('Ready to share the selected Dataverse task.', 'live');
|
|
419
|
+
} else {
|
|
420
|
+
setShareStatus('Select a task, choose a user, then grant access.', 'live');
|
|
421
|
+
}
|
|
422
|
+
renderAll();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function resetComposer() {
|
|
426
|
+
oState.sSelectedTaskId = '';
|
|
427
|
+
fillForm(null);
|
|
428
|
+
renderAll();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function buildTaskPayload() {
|
|
432
|
+
const sSubject = String(oElements.eTaskTitle.value || '').trim();
|
|
433
|
+
const sDescription = String(oElements.eTaskDescription.value || '').trim();
|
|
434
|
+
const sPriority = String(oElements.eTaskPriority.value || '1');
|
|
435
|
+
const sDueDate = String(oElements.eTaskDueDate.value || '');
|
|
436
|
+
|
|
437
|
+
if (!sSubject) {
|
|
438
|
+
throw new Error('Task subject is required.');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const oPayload = {
|
|
442
|
+
subject: sSubject,
|
|
443
|
+
description: sDescription,
|
|
444
|
+
prioritycode: Number(sPriority),
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
if (sDueDate) {
|
|
448
|
+
oPayload.scheduledend = sDueDate + 'T12:00:00Z';
|
|
449
|
+
} else {
|
|
450
|
+
oPayload.scheduledend = null;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return oPayload;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function loadCurrentUser() {
|
|
457
|
+
try {
|
|
458
|
+
oState.sCurrentUserId = normalizeGuid(await whoAmI());
|
|
459
|
+
} catch (oError) {
|
|
460
|
+
console.error('Failed to resolve current Dataverse user:', oError);
|
|
461
|
+
oState.sCurrentUserId = '';
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function loadTasks() {
|
|
466
|
+
const oResult = await listItems(sTaskTableName, sTaskPrimaryKey, {
|
|
467
|
+
select: ['activityid', 'subject', 'description', 'prioritycode', 'scheduledend', 'statecode', 'statuscode', 'modifiedon'],
|
|
468
|
+
orderBy: ['modifiedon desc'],
|
|
469
|
+
top: 100,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
oState.aTasks = (oResult.entities || []).map((oRecord) => mapTaskRecord(oRecord));
|
|
473
|
+
|
|
474
|
+
if (oState.sSelectedTaskId) {
|
|
475
|
+
const oSelected = getSelectedTask();
|
|
476
|
+
if (!oSelected) {
|
|
477
|
+
oState.sSelectedTaskId = '';
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (!oState.sSelectedTaskId && oState.aTasks.length > 0) {
|
|
482
|
+
oState.sSelectedTaskId = oState.aTasks[0].id;
|
|
483
|
+
fillForm(oState.aTasks[0]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async function loadUsers() {
|
|
488
|
+
const oResult = await listItems(sUserTableName, sUserPrimaryKey, {
|
|
489
|
+
filter: 'isdisabled eq false',
|
|
490
|
+
select: ['systemuserid', 'fullname', 'internalemailaddress', 'domainname'],
|
|
491
|
+
orderBy: ['fullname asc'],
|
|
492
|
+
top: 100,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
oState.aUsers = (oResult.entities || [])
|
|
496
|
+
.map((oRecord) => mapUserRecord(oRecord))
|
|
497
|
+
.filter((oUser) => !!oUser.id)
|
|
498
|
+
.filter((oUser) => normalizeGuid(oUser.id) !== oState.sCurrentUserId);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async function refreshWorkspace() {
|
|
502
|
+
oState.bLoading = true;
|
|
503
|
+
setStatus('Refreshing task records and share targets from Dataverse...', 'live');
|
|
504
|
+
renderAll();
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
await Promise.all([loadTasks(), loadUsers()]);
|
|
508
|
+
setStatus('Dataverse task workspace is up to date.', 'live');
|
|
509
|
+
if (oState.aUsers.length === 0) {
|
|
510
|
+
setShareStatus('No Dataverse users were returned from systemusers.', 'warning');
|
|
511
|
+
} else if (getSelectedTask()) {
|
|
512
|
+
setShareStatus('Ready to share the selected Dataverse task.', 'live');
|
|
513
|
+
} else {
|
|
514
|
+
setShareStatus('Select a task, choose a user, then grant access.', 'live');
|
|
515
|
+
}
|
|
516
|
+
} catch (oError) {
|
|
517
|
+
console.error('Failed to refresh Dataverse workspace:', oError);
|
|
518
|
+
setStatus('Failed to load Dataverse data: ' + (oError.message || String(oError)), 'error');
|
|
519
|
+
} finally {
|
|
520
|
+
oState.bLoading = false;
|
|
521
|
+
renderAll();
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function handleSaveTask(oEvent) {
|
|
526
|
+
oEvent.preventDefault();
|
|
527
|
+
oState.bSaving = true;
|
|
528
|
+
setStatus('Saving task record to Dataverse...', 'live');
|
|
529
|
+
renderActionState();
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
const oPayload = buildTaskPayload();
|
|
533
|
+
const oSelectedTask = getSelectedTask();
|
|
534
|
+
|
|
535
|
+
if (oSelectedTask) {
|
|
536
|
+
await updateItem(sTaskTableName, sTaskPrimaryKey, oSelectedTask.id, oPayload);
|
|
537
|
+
setStatus('Task record updated in Dataverse.', 'success');
|
|
538
|
+
} else {
|
|
539
|
+
const oCreated = await createItem(sTaskTableName, sTaskPrimaryKey, oPayload);
|
|
540
|
+
const sCreatedId = normalizeGuid(oCreated && (oCreated.activityid || oCreated.id || oCreated[sTaskPrimaryKey]));
|
|
541
|
+
setStatus('Task record created in Dataverse.', 'success');
|
|
542
|
+
await loadTasks();
|
|
543
|
+
if (sCreatedId) {
|
|
544
|
+
const oCreatedTask = oState.aTasks.find((oTask) => normalizeGuid(oTask.id) === sCreatedId);
|
|
545
|
+
if (oCreatedTask) {
|
|
546
|
+
oState.sSelectedTaskId = oCreatedTask.id;
|
|
547
|
+
fillForm(oCreatedTask);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
await refreshWorkspace();
|
|
553
|
+
} catch (oError) {
|
|
554
|
+
console.error('Failed to save task:', oError);
|
|
555
|
+
setStatus('Save failed: ' + (oError.message || String(oError)), 'error');
|
|
556
|
+
} finally {
|
|
557
|
+
oState.bSaving = false;
|
|
558
|
+
renderAll();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
async function handleDeleteTask() {
|
|
563
|
+
const oSelectedTask = getSelectedTask();
|
|
564
|
+
if (!oSelectedTask) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const bConfirmed = window.confirm('Delete the task "' + oSelectedTask.subject + '" from Dataverse?');
|
|
569
|
+
if (!bConfirmed) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
oState.bSaving = true;
|
|
574
|
+
setStatus('Deleting task record from Dataverse...', 'warning');
|
|
575
|
+
renderActionState();
|
|
576
|
+
|
|
577
|
+
try {
|
|
578
|
+
await deleteItem(sTaskTableName, sTaskPrimaryKey, oSelectedTask.id);
|
|
579
|
+
resetComposer();
|
|
580
|
+
await refreshWorkspace();
|
|
581
|
+
setStatus('Task record deleted from Dataverse.', 'success');
|
|
582
|
+
} catch (oError) {
|
|
583
|
+
console.error('Failed to delete task:', oError);
|
|
584
|
+
setStatus('Delete failed: ' + (oError.message || String(oError)), 'error');
|
|
585
|
+
} finally {
|
|
586
|
+
oState.bSaving = false;
|
|
587
|
+
renderAll();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function getAccessLevelById(sId) {
|
|
592
|
+
return aAccessLevels.find((oLevel) => oLevel.id === sId) || aAccessLevels[0];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function buildShareParams(sTaskId, sUserId, sMask) {
|
|
596
|
+
return {
|
|
597
|
+
Target: {
|
|
598
|
+
'@odata.type': 'Microsoft.Dynamics.CRM.task',
|
|
599
|
+
activityid: sTaskId,
|
|
600
|
+
},
|
|
601
|
+
PrincipalAccess: {
|
|
602
|
+
AccessMask: sMask,
|
|
603
|
+
Principal: {
|
|
604
|
+
'@odata.type': 'Microsoft.Dynamics.CRM.systemuser',
|
|
605
|
+
systemuserid: sUserId,
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async function shareTaskRecord(sTaskId, sUserId, sMask) {
|
|
612
|
+
const oParams = buildShareParams(sTaskId, sUserId, sMask);
|
|
613
|
+
|
|
614
|
+
try {
|
|
615
|
+
await callUnboundAction(sTaskTableName, sTaskPrimaryKey, 'GrantAccess', oParams);
|
|
616
|
+
} catch (oError) {
|
|
617
|
+
const sMessage = String(oError && oError.message ? oError.message : oError).toLowerCase();
|
|
618
|
+
const bCanFallback = sMessage.includes('already') || sMessage.includes('existing') || sMessage.includes('principal');
|
|
619
|
+
|
|
620
|
+
if (!bCanFallback) {
|
|
621
|
+
throw oError;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
await callUnboundAction(sTaskTableName, sTaskPrimaryKey, 'ModifyAccess', oParams);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async function handleShareTask() {
|
|
629
|
+
const oSelectedTask = getSelectedTask();
|
|
630
|
+
const sUserId = String(oElements.eShareUser.value || '');
|
|
631
|
+
const oAccessLevel = getAccessLevelById(String(oElements.eShareAccess.value || 'read'));
|
|
632
|
+
const oUser = oState.aUsers.find((oItem) => oItem.id === sUserId) || null;
|
|
633
|
+
|
|
634
|
+
if (!oSelectedTask) {
|
|
635
|
+
setShareStatus('Select a task before sharing it.', 'warning');
|
|
636
|
+
renderActionState();
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (!sUserId || !oUser) {
|
|
641
|
+
setShareStatus('Choose a Dataverse user to receive access.', 'warning');
|
|
642
|
+
renderActionState();
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
oState.bSharing = true;
|
|
647
|
+
setShareStatus('Granting Dataverse access to ' + oUser.fullname + '...', 'live');
|
|
648
|
+
renderActionState();
|
|
649
|
+
|
|
650
|
+
try {
|
|
651
|
+
await shareTaskRecord(oSelectedTask.id, sUserId, oAccessLevel.mask);
|
|
652
|
+
oState.aShareEvents.unshift({
|
|
653
|
+
title: oSelectedTask.subject + ' shared',
|
|
654
|
+
detail: oUser.fullname + ' received ' + oAccessLevel.label.toLowerCase() + ' on ' + formatDateTimeLabel(new Date().toISOString()) + '.',
|
|
655
|
+
});
|
|
656
|
+
oState.aShareEvents = oState.aShareEvents.slice(0, 6);
|
|
657
|
+
setShareStatus('Dataverse access granted to ' + oUser.fullname + '.', 'success');
|
|
658
|
+
} catch (oError) {
|
|
659
|
+
console.error('Failed to share task record:', oError);
|
|
660
|
+
setShareStatus('Share failed: ' + (oError.message || String(oError)), 'error');
|
|
661
|
+
} finally {
|
|
662
|
+
oState.bSharing = false;
|
|
663
|
+
renderAll();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function wireEvents() {
|
|
668
|
+
oElements.eBtnRefresh.addEventListener('click', refreshWorkspace);
|
|
669
|
+
oElements.eBtnNewTask.addEventListener('click', resetComposer);
|
|
670
|
+
oElements.eBtnResetTask.addEventListener('click', () => {
|
|
671
|
+
fillForm(getSelectedTask());
|
|
672
|
+
});
|
|
673
|
+
oElements.eBtnDeleteTask.addEventListener('click', handleDeleteTask);
|
|
674
|
+
oElements.eBtnShareTask.addEventListener('click', handleShareTask);
|
|
675
|
+
oElements.eTaskForm.addEventListener('submit', handleSaveTask);
|
|
676
|
+
oElements.eTaskSearch.addEventListener('input', renderTaskList);
|
|
677
|
+
oElements.ePriorityFilter.addEventListener('change', renderTaskList);
|
|
678
|
+
oElements.eShareUser.addEventListener('change', renderActionState);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async function boot() {
|
|
682
|
+
wireEvents();
|
|
683
|
+
initDataSources({
|
|
684
|
+
tasks: dsEntry(sTaskPrimaryKey),
|
|
685
|
+
systemusers: dsEntry(sUserPrimaryKey),
|
|
686
|
+
});
|
|
687
|
+
renderAll();
|
|
688
|
+
fillForm(null);
|
|
689
|
+
await loadCurrentUser();
|
|
690
|
+
await refreshWorkspace();
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
boot();
|