@darkwheel/pptb-solution-explorer 0.2.1-beta.0 → 0.2.2
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/README.md +28 -28
- package/dist/app.js +221 -221
- package/dist/index.html +91 -91
- package/dist/styles.css +564 -564
- package/package.json +43 -44
package/README.md
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
# Solution Explorer
|
|
2
|
-
|
|
3
|
-
A comprehensive Power Platform Tool Box tool for exploring and managing Dataverse solutions.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Solution Listing**: View all solutions in your Dataverse environment
|
|
8
|
-
- **Detailed Metadata**: See comprehensive information about each solution including:
|
|
9
|
-
- Solution name, version, and description
|
|
10
|
-
- Publisher information
|
|
11
|
-
- Installation and modification dates
|
|
12
|
-
- Managed vs. unmanaged status
|
|
13
|
-
- Solution components count
|
|
14
|
-
- Package type and version information
|
|
15
|
-
- **Publisher Details**: View publisher information including prefix and option value prefix
|
|
16
|
-
- **Filter & Search**: Easily find specific solutions
|
|
17
|
-
- **Management Ready**: Quick access to solution metadata for administrative tasks
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
20
|
-
|
|
21
|
-
1. Connect to your Dataverse environment in Power Platform Tool Box
|
|
22
|
-
2. Open the Solution Explorer tool
|
|
23
|
-
3. Click "Load Solutions" to retrieve all solutions
|
|
24
|
-
4. Click on any solution to view detailed information
|
|
25
|
-
|
|
26
|
-
## Requirements
|
|
27
|
-
|
|
28
|
-
- Power Platform Tool Box
|
|
1
|
+
# Solution Explorer
|
|
2
|
+
|
|
3
|
+
A comprehensive Power Platform Tool Box tool for exploring and managing Dataverse solutions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Solution Listing**: View all solutions in your Dataverse environment
|
|
8
|
+
- **Detailed Metadata**: See comprehensive information about each solution including:
|
|
9
|
+
- Solution name, version, and description
|
|
10
|
+
- Publisher information
|
|
11
|
+
- Installation and modification dates
|
|
12
|
+
- Managed vs. unmanaged status
|
|
13
|
+
- Solution components count
|
|
14
|
+
- Package type and version information
|
|
15
|
+
- **Publisher Details**: View publisher information including prefix and option value prefix
|
|
16
|
+
- **Filter & Search**: Easily find specific solutions
|
|
17
|
+
- **Management Ready**: Quick access to solution metadata for administrative tasks
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
1. Connect to your Dataverse environment in Power Platform Tool Box
|
|
22
|
+
2. Open the Solution Explorer tool
|
|
23
|
+
3. Click "Load Solutions" to retrieve all solutions
|
|
24
|
+
4. Click on any solution to view detailed information
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- Power Platform Tool Box
|
|
29
29
|
- Dataverse connection with appropriate permissions to read solution metadata
|
package/dist/app.js
CHANGED
|
@@ -123,38 +123,38 @@ async function loadSolutions() {
|
|
|
123
123
|
await toolbox.utils.showLoading('Loading solutions...');
|
|
124
124
|
// FetchXML to get solutions with publisher information (dynamically set top value)
|
|
125
125
|
const topAttribute = currentLimit === 'all' ? '' : `top="${currentLimit}"`;
|
|
126
|
-
const fetchXml = `
|
|
127
|
-
<fetch ${topAttribute}>
|
|
128
|
-
<entity name="solution">
|
|
129
|
-
<attribute name="solutionid" />
|
|
130
|
-
<attribute name="uniquename" />
|
|
131
|
-
<attribute name="friendlyname" />
|
|
132
|
-
<attribute name="version" />
|
|
133
|
-
<attribute name="description" />
|
|
134
|
-
<attribute name="installedon" />
|
|
135
|
-
<attribute name="createdon" />
|
|
136
|
-
<attribute name="modifiedon" />
|
|
137
|
-
<attribute name="ismanaged" />
|
|
138
|
-
<attribute name="isvisible" />
|
|
139
|
-
<attribute name="solutionpackageversion" />
|
|
140
|
-
<attribute name="solutiontype" />
|
|
141
|
-
<attribute name="publisherid" />
|
|
142
|
-
<link-entity name="publisher" from="publisherid" to="publisherid" alias="publisher">
|
|
143
|
-
<attribute name="friendlyname" />
|
|
144
|
-
<attribute name="uniquename" />
|
|
145
|
-
<attribute name="customizationprefix" />
|
|
146
|
-
<attribute name="customizationoptionvalueprefix" />
|
|
147
|
-
<attribute name="description" />
|
|
148
|
-
</link-entity>
|
|
149
|
-
<link-entity name="systemuser" from="systemuserid" to="modifiedby" alias="modifiedby">
|
|
150
|
-
<attribute name="fullname" />
|
|
151
|
-
<attribute name="domainname" />
|
|
152
|
-
</link-entity>
|
|
153
|
-
<filter>
|
|
154
|
-
<condition attribute="isvisible" operator="eq" value="1" />
|
|
155
|
-
</filter>
|
|
156
|
-
<order attribute="installedon" descending="true" />
|
|
157
|
-
</entity>
|
|
126
|
+
const fetchXml = `
|
|
127
|
+
<fetch ${topAttribute}>
|
|
128
|
+
<entity name="solution">
|
|
129
|
+
<attribute name="solutionid" />
|
|
130
|
+
<attribute name="uniquename" />
|
|
131
|
+
<attribute name="friendlyname" />
|
|
132
|
+
<attribute name="version" />
|
|
133
|
+
<attribute name="description" />
|
|
134
|
+
<attribute name="installedon" />
|
|
135
|
+
<attribute name="createdon" />
|
|
136
|
+
<attribute name="modifiedon" />
|
|
137
|
+
<attribute name="ismanaged" />
|
|
138
|
+
<attribute name="isvisible" />
|
|
139
|
+
<attribute name="solutionpackageversion" />
|
|
140
|
+
<attribute name="solutiontype" />
|
|
141
|
+
<attribute name="publisherid" />
|
|
142
|
+
<link-entity name="publisher" from="publisherid" to="publisherid" alias="publisher">
|
|
143
|
+
<attribute name="friendlyname" />
|
|
144
|
+
<attribute name="uniquename" />
|
|
145
|
+
<attribute name="customizationprefix" />
|
|
146
|
+
<attribute name="customizationoptionvalueprefix" />
|
|
147
|
+
<attribute name="description" />
|
|
148
|
+
</link-entity>
|
|
149
|
+
<link-entity name="systemuser" from="systemuserid" to="modifiedby" alias="modifiedby">
|
|
150
|
+
<attribute name="fullname" />
|
|
151
|
+
<attribute name="domainname" />
|
|
152
|
+
</link-entity>
|
|
153
|
+
<filter>
|
|
154
|
+
<condition attribute="isvisible" operator="eq" value="1" />
|
|
155
|
+
</filter>
|
|
156
|
+
<order attribute="installedon" descending="true" />
|
|
157
|
+
</entity>
|
|
158
158
|
</fetch>`;
|
|
159
159
|
const result = await dataverse.fetchXmlQuery(fetchXml);
|
|
160
160
|
allSolutions = result.value;
|
|
@@ -235,18 +235,18 @@ function displaySolutions() {
|
|
|
235
235
|
const isManaged = solution.ismanaged;
|
|
236
236
|
const typeLabel = isManaged ? 'Managed' : 'Unmanaged';
|
|
237
237
|
const typeClass = isManaged ? 'managed' : 'unmanaged';
|
|
238
|
-
return `
|
|
239
|
-
<div class="solution-card ${typeClass}" data-solutionid="${solution.solutionid}">
|
|
240
|
-
<div class="solution-header">
|
|
241
|
-
<div class="solution-name">${escapeHtml(solution.friendlyname || solution.uniquename || 'N/A')}</div>
|
|
242
|
-
<span class="solution-type-badge ${typeClass}">${typeLabel}</span>
|
|
243
|
-
</div>
|
|
244
|
-
<div class="solution-meta">
|
|
245
|
-
<div class="solution-version">v${escapeHtml(solution.version || '1.0.0.0')}</div>
|
|
246
|
-
<div class="solution-publisher">${escapeHtml(solution['publisher.friendlyname'] || 'N/A')}</div>
|
|
247
|
-
</div>
|
|
248
|
-
<div class="solution-unique-name">${escapeHtml(solution.uniquename || 'N/A')}</div>
|
|
249
|
-
</div>
|
|
238
|
+
return `
|
|
239
|
+
<div class="solution-card ${typeClass}" data-solutionid="${solution.solutionid}">
|
|
240
|
+
<div class="solution-header">
|
|
241
|
+
<div class="solution-name">${escapeHtml(solution.friendlyname || solution.uniquename || 'N/A')}</div>
|
|
242
|
+
<span class="solution-type-badge ${typeClass}">${typeLabel}</span>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="solution-meta">
|
|
245
|
+
<div class="solution-version">v${escapeHtml(solution.version || '1.0.0.0')}</div>
|
|
246
|
+
<div class="solution-publisher">${escapeHtml(solution['publisher.friendlyname'] || 'N/A')}</div>
|
|
247
|
+
</div>
|
|
248
|
+
<div class="solution-unique-name">${escapeHtml(solution.uniquename || 'N/A')}</div>
|
|
249
|
+
</div>
|
|
250
250
|
`;
|
|
251
251
|
}).join('');
|
|
252
252
|
// Add click handlers
|
|
@@ -298,14 +298,14 @@ async function selectSolution(solutionId, solutionData) {
|
|
|
298
298
|
*/
|
|
299
299
|
async function getSolutionComponentCount(solutionId) {
|
|
300
300
|
try {
|
|
301
|
-
const fetchXml = `
|
|
302
|
-
<fetch aggregate="true">
|
|
303
|
-
<entity name="solutioncomponent">
|
|
304
|
-
<attribute name="solutioncomponentid" alias="count" aggregate="count" />
|
|
305
|
-
<filter>
|
|
306
|
-
<condition attribute="solutionid" operator="eq" value="${solutionId}" />
|
|
307
|
-
</filter>
|
|
308
|
-
</entity>
|
|
301
|
+
const fetchXml = `
|
|
302
|
+
<fetch aggregate="true">
|
|
303
|
+
<entity name="solutioncomponent">
|
|
304
|
+
<attribute name="solutioncomponentid" alias="count" aggregate="count" />
|
|
305
|
+
<filter>
|
|
306
|
+
<condition attribute="solutionid" operator="eq" value="${solutionId}" />
|
|
307
|
+
</filter>
|
|
308
|
+
</entity>
|
|
309
309
|
</fetch>`;
|
|
310
310
|
const result = await dataverse.fetchXmlQuery(fetchXml);
|
|
311
311
|
return parseInt(result.value[0]?.count || '0', 10);
|
|
@@ -320,22 +320,22 @@ async function getSolutionComponentCount(solutionId) {
|
|
|
320
320
|
*/
|
|
321
321
|
async function getSolutionDeploymentHistory(solutionId) {
|
|
322
322
|
try {
|
|
323
|
-
const fetchXml = `
|
|
324
|
-
<fetch top="20">
|
|
325
|
-
<entity name="importjob">
|
|
326
|
-
<attribute name="importjobid" />
|
|
327
|
-
<attribute name="solutionname" />
|
|
328
|
-
<attribute name="completedon" />
|
|
329
|
-
<attribute name="startedon" />
|
|
330
|
-
<attribute name="progress" />
|
|
331
|
-
<attribute name="createdonbehalfby" />
|
|
332
|
-
<attribute name="createdby" />
|
|
333
|
-
<attribute name="modifiedby" />
|
|
334
|
-
<filter>
|
|
335
|
-
<condition attribute="solutionid" operator="eq" value="${solutionId}" />
|
|
336
|
-
</filter>
|
|
337
|
-
<order attribute="completedon" descending="true" />
|
|
338
|
-
</entity>
|
|
323
|
+
const fetchXml = `
|
|
324
|
+
<fetch top="20">
|
|
325
|
+
<entity name="importjob">
|
|
326
|
+
<attribute name="importjobid" />
|
|
327
|
+
<attribute name="solutionname" />
|
|
328
|
+
<attribute name="completedon" />
|
|
329
|
+
<attribute name="startedon" />
|
|
330
|
+
<attribute name="progress" />
|
|
331
|
+
<attribute name="createdonbehalfby" />
|
|
332
|
+
<attribute name="createdby" />
|
|
333
|
+
<attribute name="modifiedby" />
|
|
334
|
+
<filter>
|
|
335
|
+
<condition attribute="solutionid" operator="eq" value="${solutionId}" />
|
|
336
|
+
</filter>
|
|
337
|
+
<order attribute="completedon" descending="true" />
|
|
338
|
+
</entity>
|
|
339
339
|
</fetch>`;
|
|
340
340
|
const result = await dataverse.fetchXmlQuery(fetchXml);
|
|
341
341
|
return result.value || [];
|
|
@@ -355,162 +355,162 @@ function displaySolutionDetails(solution, componentCount, deploymentHistory = []
|
|
|
355
355
|
const isManaged = solution.ismanaged;
|
|
356
356
|
const typeLabel = isManaged ? 'Managed' : 'Unmanaged';
|
|
357
357
|
const typeClass = isManaged ? 'managed' : 'unmanaged';
|
|
358
|
-
detailsPanel.innerHTML = `
|
|
359
|
-
<div class="solution-details">
|
|
360
|
-
<div class="details-header">
|
|
361
|
-
<h3>${escapeHtml(solution.friendlyname || solution.uniquename || 'N/A')}</h3>
|
|
362
|
-
<span class="solution-type-badge ${typeClass}">${typeLabel}</span>
|
|
363
|
-
</div>
|
|
364
|
-
|
|
365
|
-
<div class="details-section">
|
|
366
|
-
<h4>Basic Information</h4>
|
|
367
|
-
<table class="details-table">
|
|
368
|
-
<tr>
|
|
369
|
-
<td class="label">Display Name:</td>
|
|
370
|
-
<td>${escapeHtml(solution.friendlyname || 'N/A')}</td>
|
|
371
|
-
</tr>
|
|
372
|
-
<tr>
|
|
373
|
-
<td class="label">Unique Name:</td>
|
|
374
|
-
<td><code>${escapeHtml(solution.uniquename || 'N/A')}</code></td>
|
|
375
|
-
</tr>
|
|
376
|
-
<tr>
|
|
377
|
-
<td class="label">Version:</td>
|
|
378
|
-
<td>${escapeHtml(solution.version || '1.0.0.0')}</td>
|
|
379
|
-
</tr>
|
|
380
|
-
<tr>
|
|
381
|
-
<td class="label">Description:</td>
|
|
382
|
-
<td>${escapeHtml(solution.description || 'No description provided')}</td>
|
|
383
|
-
</tr>
|
|
384
|
-
<tr>
|
|
385
|
-
<td class="label">Solution ID:</td>
|
|
386
|
-
<td><code>${escapeHtml(solution.solutionid || 'N/A')}</code></td>
|
|
387
|
-
</tr>
|
|
388
|
-
</table>
|
|
389
|
-
</div>
|
|
390
|
-
|
|
391
|
-
<div class="details-section">
|
|
392
|
-
<h4>Publisher Information</h4>
|
|
393
|
-
<table class="details-table">
|
|
394
|
-
<tr>
|
|
395
|
-
<td class="label">Publisher Name:</td>
|
|
396
|
-
<td>${escapeHtml(solution['publisher.friendlyname'] || 'N/A')}</td>
|
|
397
|
-
</tr>
|
|
398
|
-
<tr>
|
|
399
|
-
<td class="label">Publisher Unique Name:</td>
|
|
400
|
-
<td><code>${escapeHtml(solution['publisher.uniquename'] || 'N/A')}</code></td>
|
|
401
|
-
</tr>
|
|
402
|
-
<tr>
|
|
403
|
-
<td class="label">Customization Prefix:</td>
|
|
404
|
-
<td><code>${escapeHtml(solution['publisher.customizationprefix'] || 'N/A')}</code></td>
|
|
405
|
-
</tr>
|
|
406
|
-
<tr>
|
|
407
|
-
<td class="label">Option Value Prefix:</td>
|
|
408
|
-
<td>${solution['publisher.customizationoptionvalueprefix'] !== null && solution['publisher.customizationoptionvalueprefix'] !== undefined ? solution['publisher.customizationoptionvalueprefix'] : 'N/A'}</td>
|
|
409
|
-
</tr>
|
|
410
|
-
${solution['publisher.description'] ? `
|
|
411
|
-
<tr>
|
|
412
|
-
<td class="label">Publisher Description:</td>
|
|
413
|
-
<td>${escapeHtml(solution['publisher.description'])}</td>
|
|
414
|
-
</tr>
|
|
415
|
-
` : ''}
|
|
416
|
-
</table>
|
|
417
|
-
</div>
|
|
418
|
-
|
|
419
|
-
<div class="details-section">
|
|
420
|
-
<h4>Installation & Deployment</h4>
|
|
421
|
-
<table class="details-table">
|
|
422
|
-
<tr>
|
|
423
|
-
<td class="label">Installed On:</td>
|
|
424
|
-
<td>${solution.installedon ? formatDateTime(solution.installedon) : 'N/A'}</td>
|
|
425
|
-
</tr>
|
|
426
|
-
<tr>
|
|
427
|
-
<td class="label">Created On:</td>
|
|
428
|
-
<td>${solution.createdon ? formatDateTime(solution.createdon) : 'N/A'}</td>
|
|
429
|
-
</tr>
|
|
430
|
-
<tr>
|
|
431
|
-
<td class="label">Modified On:</td>
|
|
432
|
-
<td>${solution.modifiedon ? formatDateTime(solution.modifiedon) : 'N/A'}</td>
|
|
433
|
-
</tr>
|
|
434
|
-
<tr>
|
|
435
|
-
<td class="label">Last Modified By:</td>
|
|
436
|
-
<td>
|
|
437
|
-
<div class="modifier-info">
|
|
438
|
-
<div class="modifier-name">${escapeHtml(solution['modifiedby.fullname'] || 'N/A')}</div>
|
|
439
|
-
${solution['modifiedby.domainname'] ? `<div class="modifier-domain">${escapeHtml(solution['modifiedby.domainname'])}</div>` : ''}
|
|
440
|
-
</div>
|
|
441
|
-
</td>
|
|
442
|
-
</tr>
|
|
443
|
-
<tr>
|
|
444
|
-
<td class="label">Package Type:</td>
|
|
445
|
-
<td>${isManaged ? 'Managed' : 'Unmanaged'}</td>
|
|
446
|
-
</tr>
|
|
447
|
-
<tr>
|
|
448
|
-
<td class="label">Is Visible:</td>
|
|
449
|
-
<td>${solution.isvisible ? 'Yes' : 'No'}</td>
|
|
450
|
-
</tr>
|
|
451
|
-
${solution.solutionpackageversion ? `
|
|
452
|
-
<tr>
|
|
453
|
-
<td class="label">Package Version:</td>
|
|
454
|
-
<td>${escapeHtml(solution.solutionpackageversion)}</td>
|
|
455
|
-
</tr>
|
|
456
|
-
` : ''}
|
|
457
|
-
</table>
|
|
458
|
-
</div>
|
|
459
|
-
|
|
460
|
-
<div class="details-section">
|
|
461
|
-
<h4>Components</h4>
|
|
462
|
-
<table class="details-table">
|
|
463
|
-
<tr>
|
|
464
|
-
<td class="label">Component Count:</td>
|
|
465
|
-
<td><strong>${componentCount}</strong> component(s)</td>
|
|
466
|
-
</tr>
|
|
467
|
-
</table>
|
|
468
|
-
</div>
|
|
469
|
-
|
|
470
|
-
${deploymentHistory.length > 0 ? `
|
|
471
|
-
<div class="details-section">
|
|
472
|
-
<h4>Deployment History</h4>
|
|
473
|
-
<div class="deployment-history">
|
|
358
|
+
detailsPanel.innerHTML = `
|
|
359
|
+
<div class="solution-details">
|
|
360
|
+
<div class="details-header">
|
|
361
|
+
<h3>${escapeHtml(solution.friendlyname || solution.uniquename || 'N/A')}</h3>
|
|
362
|
+
<span class="solution-type-badge ${typeClass}">${typeLabel}</span>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<div class="details-section">
|
|
366
|
+
<h4>Basic Information</h4>
|
|
367
|
+
<table class="details-table">
|
|
368
|
+
<tr>
|
|
369
|
+
<td class="label">Display Name:</td>
|
|
370
|
+
<td>${escapeHtml(solution.friendlyname || 'N/A')}</td>
|
|
371
|
+
</tr>
|
|
372
|
+
<tr>
|
|
373
|
+
<td class="label">Unique Name:</td>
|
|
374
|
+
<td><code>${escapeHtml(solution.uniquename || 'N/A')}</code></td>
|
|
375
|
+
</tr>
|
|
376
|
+
<tr>
|
|
377
|
+
<td class="label">Version:</td>
|
|
378
|
+
<td>${escapeHtml(solution.version || '1.0.0.0')}</td>
|
|
379
|
+
</tr>
|
|
380
|
+
<tr>
|
|
381
|
+
<td class="label">Description:</td>
|
|
382
|
+
<td>${escapeHtml(solution.description || 'No description provided')}</td>
|
|
383
|
+
</tr>
|
|
384
|
+
<tr>
|
|
385
|
+
<td class="label">Solution ID:</td>
|
|
386
|
+
<td><code>${escapeHtml(solution.solutionid || 'N/A')}</code></td>
|
|
387
|
+
</tr>
|
|
388
|
+
</table>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<div class="details-section">
|
|
392
|
+
<h4>Publisher Information</h4>
|
|
393
|
+
<table class="details-table">
|
|
394
|
+
<tr>
|
|
395
|
+
<td class="label">Publisher Name:</td>
|
|
396
|
+
<td>${escapeHtml(solution['publisher.friendlyname'] || 'N/A')}</td>
|
|
397
|
+
</tr>
|
|
398
|
+
<tr>
|
|
399
|
+
<td class="label">Publisher Unique Name:</td>
|
|
400
|
+
<td><code>${escapeHtml(solution['publisher.uniquename'] || 'N/A')}</code></td>
|
|
401
|
+
</tr>
|
|
402
|
+
<tr>
|
|
403
|
+
<td class="label">Customization Prefix:</td>
|
|
404
|
+
<td><code>${escapeHtml(solution['publisher.customizationprefix'] || 'N/A')}</code></td>
|
|
405
|
+
</tr>
|
|
406
|
+
<tr>
|
|
407
|
+
<td class="label">Option Value Prefix:</td>
|
|
408
|
+
<td>${solution['publisher.customizationoptionvalueprefix'] !== null && solution['publisher.customizationoptionvalueprefix'] !== undefined ? solution['publisher.customizationoptionvalueprefix'] : 'N/A'}</td>
|
|
409
|
+
</tr>
|
|
410
|
+
${solution['publisher.description'] ? `
|
|
411
|
+
<tr>
|
|
412
|
+
<td class="label">Publisher Description:</td>
|
|
413
|
+
<td>${escapeHtml(solution['publisher.description'])}</td>
|
|
414
|
+
</tr>
|
|
415
|
+
` : ''}
|
|
416
|
+
</table>
|
|
417
|
+
</div>
|
|
418
|
+
|
|
419
|
+
<div class="details-section">
|
|
420
|
+
<h4>Installation & Deployment</h4>
|
|
421
|
+
<table class="details-table">
|
|
422
|
+
<tr>
|
|
423
|
+
<td class="label">Installed On:</td>
|
|
424
|
+
<td>${solution.installedon ? formatDateTime(solution.installedon) : 'N/A'}</td>
|
|
425
|
+
</tr>
|
|
426
|
+
<tr>
|
|
427
|
+
<td class="label">Created On:</td>
|
|
428
|
+
<td>${solution.createdon ? formatDateTime(solution.createdon) : 'N/A'}</td>
|
|
429
|
+
</tr>
|
|
430
|
+
<tr>
|
|
431
|
+
<td class="label">Modified On:</td>
|
|
432
|
+
<td>${solution.modifiedon ? formatDateTime(solution.modifiedon) : 'N/A'}</td>
|
|
433
|
+
</tr>
|
|
434
|
+
<tr>
|
|
435
|
+
<td class="label">Last Modified By:</td>
|
|
436
|
+
<td>
|
|
437
|
+
<div class="modifier-info">
|
|
438
|
+
<div class="modifier-name">${escapeHtml(solution['modifiedby.fullname'] || 'N/A')}</div>
|
|
439
|
+
${solution['modifiedby.domainname'] ? `<div class="modifier-domain">${escapeHtml(solution['modifiedby.domainname'])}</div>` : ''}
|
|
440
|
+
</div>
|
|
441
|
+
</td>
|
|
442
|
+
</tr>
|
|
443
|
+
<tr>
|
|
444
|
+
<td class="label">Package Type:</td>
|
|
445
|
+
<td>${isManaged ? 'Managed' : 'Unmanaged'}</td>
|
|
446
|
+
</tr>
|
|
447
|
+
<tr>
|
|
448
|
+
<td class="label">Is Visible:</td>
|
|
449
|
+
<td>${solution.isvisible ? 'Yes' : 'No'}</td>
|
|
450
|
+
</tr>
|
|
451
|
+
${solution.solutionpackageversion ? `
|
|
452
|
+
<tr>
|
|
453
|
+
<td class="label">Package Version:</td>
|
|
454
|
+
<td>${escapeHtml(solution.solutionpackageversion)}</td>
|
|
455
|
+
</tr>
|
|
456
|
+
` : ''}
|
|
457
|
+
</table>
|
|
458
|
+
</div>
|
|
459
|
+
|
|
460
|
+
<div class="details-section">
|
|
461
|
+
<h4>Components</h4>
|
|
462
|
+
<table class="details-table">
|
|
463
|
+
<tr>
|
|
464
|
+
<td class="label">Component Count:</td>
|
|
465
|
+
<td><strong>${componentCount}</strong> component(s)</td>
|
|
466
|
+
</tr>
|
|
467
|
+
</table>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
470
|
+
${deploymentHistory.length > 0 ? `
|
|
471
|
+
<div class="details-section">
|
|
472
|
+
<h4>Deployment History</h4>
|
|
473
|
+
<div class="deployment-history">
|
|
474
474
|
${deploymentHistory.map((deployment, index) => {
|
|
475
475
|
const completedOn = deployment.completedon ? formatDateTime(deployment.completedon) : 'N/A';
|
|
476
476
|
const startedOn = deployment.startedon ? formatDateTime(deployment.startedon) : 'N/A';
|
|
477
477
|
const deployedBy = escapeHtml(deployment['_createdby_value@OData.Community.Display.V1.FormattedValue'] || 'System');
|
|
478
478
|
const progress = deployment.progress !== null && deployment.progress !== undefined ? Math.round(deployment.progress) : 100;
|
|
479
|
-
return `
|
|
480
|
-
<div class="deployment-item">
|
|
481
|
-
<div class="deployment-header">
|
|
482
|
-
<div class="deployment-number">#${index + 1}</div>
|
|
483
|
-
<div class="deployment-date">${completedOn}</div>
|
|
484
|
-
</div>
|
|
485
|
-
<div class="deployment-details">
|
|
486
|
-
<div class="deployment-info">
|
|
487
|
-
<span class="label">Deployed By:</span>
|
|
488
|
-
<span class="value">${deployedBy}</span>
|
|
489
|
-
</div>
|
|
490
|
-
<div class="deployment-info">
|
|
491
|
-
<span class="label">Started:</span>
|
|
492
|
-
<span class="value">${startedOn}</span>
|
|
493
|
-
</div>
|
|
494
|
-
<div class="deployment-info">
|
|
495
|
-
<span class="label">Progress:</span>
|
|
496
|
-
<span class="value">${progress}%</span>
|
|
497
|
-
</div>
|
|
498
|
-
</div>
|
|
499
|
-
</div>
|
|
479
|
+
return `
|
|
480
|
+
<div class="deployment-item">
|
|
481
|
+
<div class="deployment-header">
|
|
482
|
+
<div class="deployment-number">#${index + 1}</div>
|
|
483
|
+
<div class="deployment-date">${completedOn}</div>
|
|
484
|
+
</div>
|
|
485
|
+
<div class="deployment-details">
|
|
486
|
+
<div class="deployment-info">
|
|
487
|
+
<span class="label">Deployed By:</span>
|
|
488
|
+
<span class="value">${deployedBy}</span>
|
|
489
|
+
</div>
|
|
490
|
+
<div class="deployment-info">
|
|
491
|
+
<span class="label">Started:</span>
|
|
492
|
+
<span class="value">${startedOn}</span>
|
|
493
|
+
</div>
|
|
494
|
+
<div class="deployment-info">
|
|
495
|
+
<span class="label">Progress:</span>
|
|
496
|
+
<span class="value">${progress}%</span>
|
|
497
|
+
</div>
|
|
498
|
+
</div>
|
|
499
|
+
</div>
|
|
500
500
|
`;
|
|
501
|
-
}).join('')}
|
|
502
|
-
</div>
|
|
503
|
-
</div>
|
|
504
|
-
` : ''}
|
|
505
|
-
|
|
506
|
-
<div class="details-section">
|
|
507
|
-
<h4>Management Actions</h4>
|
|
508
|
-
<div class="action-buttons">
|
|
509
|
-
<button class="btn btn-secondary btn-small" onclick="copySolutionId('${solution.solutionid}')">Copy Solution ID</button>
|
|
510
|
-
<button class="btn btn-secondary btn-small" onclick="copySolutionUniqueName('${solution.uniquename}')">Copy Unique Name</button>
|
|
511
|
-
</div>
|
|
512
|
-
</div>
|
|
513
|
-
</div>
|
|
501
|
+
}).join('')}
|
|
502
|
+
</div>
|
|
503
|
+
</div>
|
|
504
|
+
` : ''}
|
|
505
|
+
|
|
506
|
+
<div class="details-section">
|
|
507
|
+
<h4>Management Actions</h4>
|
|
508
|
+
<div class="action-buttons">
|
|
509
|
+
<button class="btn btn-secondary btn-small" onclick="copySolutionId('${solution.solutionid}')">Copy Solution ID</button>
|
|
510
|
+
<button class="btn btn-secondary btn-small" onclick="copySolutionUniqueName('${solution.uniquename}')">Copy Unique Name</button>
|
|
511
|
+
</div>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
514
|
`;
|
|
515
515
|
}
|
|
516
516
|
/**
|
|
@@ -582,9 +582,9 @@ function log(message, type = 'info') {
|
|
|
582
582
|
const timestamp = new Date().toLocaleTimeString();
|
|
583
583
|
const logEntry = document.createElement('div');
|
|
584
584
|
logEntry.className = `log-entry ${type}`;
|
|
585
|
-
logEntry.innerHTML = `
|
|
586
|
-
<span class="log-timestamp">[${timestamp}]</span>
|
|
587
|
-
<span>${message}</span>
|
|
585
|
+
logEntry.innerHTML = `
|
|
586
|
+
<span class="log-timestamp">[${timestamp}]</span>
|
|
587
|
+
<span>${message}</span>
|
|
588
588
|
`;
|
|
589
589
|
logDiv.insertBefore(logEntry, logDiv.firstChild);
|
|
590
590
|
// Keep only last 50 entries
|