@datagrok/hit-triage 1.3.3 → 1.3.4
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/CHANGELOG.md +5 -0
- package/README_HD.md +8 -0
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +1 -1
- package/src/app/consts.ts +13 -5
- package/src/app/hit-design-app.ts +17 -11
- package/src/app/hit-design-views/info-view.ts +44 -6
- package/src/app/hit-design-views/submit-view.ts +41 -5
- package/src/app/hit-design-views/utils.css +4 -0
- package/src/app/pepti-hit-app.ts +1 -1
- package/src/app/pepti-hits-views/info-view.ts +1 -1
- package/src/app/types.ts +1 -1
- package/src/app/utils.ts +82 -2
package/package.json
CHANGED
package/src/app/consts.ts
CHANGED
|
@@ -17,17 +17,17 @@ export const HTScriptPrefix = 'HTScript';
|
|
|
17
17
|
export const HTQueryPrefix = 'HTQuery';
|
|
18
18
|
export const ComputeQueryMolColName = 'molecules';
|
|
19
19
|
export const i18n = {
|
|
20
|
-
startNewCampaign: 'New
|
|
21
|
-
createNewCampaign: 'New
|
|
20
|
+
startNewCampaign: 'New Campaign',
|
|
21
|
+
createNewCampaign: 'New Campaign',
|
|
22
22
|
dataSourceFunction: 'Source',
|
|
23
|
-
createNewTemplate: 'New
|
|
23
|
+
createNewTemplate: 'New Template',
|
|
24
24
|
StartCampaign: 'Start',
|
|
25
25
|
createTemplate: 'Create',
|
|
26
26
|
createCampaign: 'Create',
|
|
27
27
|
download: 'Download',
|
|
28
28
|
cancel: 'Cancel',
|
|
29
|
-
continueCampaigns: 'Continue
|
|
30
|
-
createNewCampaignHeader: 'New
|
|
29
|
+
continueCampaigns: 'Continue Campaign',
|
|
30
|
+
createNewCampaignHeader: 'New Campaign',
|
|
31
31
|
selectTemplate: 'Template',
|
|
32
32
|
} as const;
|
|
33
33
|
|
|
@@ -36,3 +36,11 @@ export const funcTypeNames = {
|
|
|
36
36
|
function: 'function-package',
|
|
37
37
|
query: 'data-query',
|
|
38
38
|
} as const;
|
|
39
|
+
|
|
40
|
+
export const HDCampaignsGroupingLSKey = 'HDCampaignsGrouping';
|
|
41
|
+
|
|
42
|
+
export enum CampaignGroupingType {
|
|
43
|
+
None = 'None',
|
|
44
|
+
Template = 'Template',
|
|
45
|
+
Status = 'Status',
|
|
46
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
|
-
import {AppName, HitDesignCampaign, HitDesignTemplate,
|
|
5
|
+
import {AppName, HitDesignCampaign, HitDesignTemplate, IFunctionArgs} from './types';
|
|
6
6
|
import {HitDesignInfoView} from './hit-design-views/info-view';
|
|
7
7
|
import {CampaignIdKey, CampaignJsonName, CampaignTableName,
|
|
8
8
|
HTQueryPrefix, HTScriptPrefix, HitDesignCampaignIdKey,
|
|
@@ -36,6 +36,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
36
36
|
protected currentDesignViewId?: string;
|
|
37
37
|
public mainView: DG.ViewBase;
|
|
38
38
|
protected get version() {return this._campaign?.version ?? 0;};
|
|
39
|
+
public existingStatuses: string[] = [];
|
|
39
40
|
constructor(c: DG.FuncCall, an: AppName = 'Hit Design',
|
|
40
41
|
infoViewConstructor: (app: HitDesignApp) => HitDesignInfoView = (app) => new HitDesignInfoView(app)) {
|
|
41
42
|
super(c, an);
|
|
@@ -112,7 +113,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
112
113
|
|
|
113
114
|
this.campaign.template.stages = uniqueStages;
|
|
114
115
|
this.template.stages = uniqueStages;
|
|
115
|
-
await this.saveCampaign(
|
|
116
|
+
await this.saveCampaign(true);
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
public async setTemplate(template: T, campaignId?: string) {
|
|
@@ -226,7 +227,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
226
227
|
this.dataFrame!.col(col.name)!.set(newValueIdx, col.get(0), false);
|
|
227
228
|
}
|
|
228
229
|
this.dataFrame!.fireValuesChanged();
|
|
229
|
-
this.saveCampaign(
|
|
230
|
+
this.saveCampaign(false);
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
protected initDesignViewRibbons(view: DG.TableView, subs: Subscription[]) {
|
|
@@ -318,7 +319,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
318
319
|
this.dataFrame!.fireValuesChanged();
|
|
319
320
|
} finally {
|
|
320
321
|
ui.setUpdateIndicator(view.grid.root, false);
|
|
321
|
-
this.saveCampaign(
|
|
322
|
+
this.saveCampaign(false);
|
|
322
323
|
}
|
|
323
324
|
}, () => null, this.campaign?.template!, true);
|
|
324
325
|
};
|
|
@@ -328,7 +329,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
328
329
|
const permissionsButton = ui.iconFA('share', async () => {
|
|
329
330
|
await (new PermissionsDialog(this.campaign?.permissions)).show((res) => {
|
|
330
331
|
this.campaign!.permissions = res;
|
|
331
|
-
this.saveCampaign(
|
|
332
|
+
this.saveCampaign(true);
|
|
332
333
|
});
|
|
333
334
|
}, 'Edit campaign permissions');
|
|
334
335
|
const tilesButton = ui.bigButton('Progress tracker', () => {
|
|
@@ -340,8 +341,13 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
340
341
|
if (dialogContent) {
|
|
341
342
|
const dlg = ui.dialog('Submit');
|
|
342
343
|
dlg.add(dialogContent);
|
|
343
|
-
dlg.addButton('Save', ()=>{
|
|
344
|
-
|
|
344
|
+
dlg.addButton('Save', () => {
|
|
345
|
+
this._campaign!.status = this._submitView!.getStatus();
|
|
346
|
+
this.saveCampaign();
|
|
347
|
+
dlg.close();
|
|
348
|
+
});
|
|
349
|
+
if (this.template?.submit?.fName && this.template?.submit?.package && DG.Func.find({name: this.template.submit.fName, package: this.template.submit.package})?.length > 0)
|
|
350
|
+
dlg.addButton('Submit', ()=>{this._submitView?.submit(); dlg.close();});
|
|
345
351
|
dlg.show();
|
|
346
352
|
}
|
|
347
353
|
});
|
|
@@ -476,7 +482,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
476
482
|
view?.grid && subs.push(view.grid.onCellValueEdited.subscribe(async (gc) => {
|
|
477
483
|
try {
|
|
478
484
|
if (gc.tableColumn?.name === TileCategoriesColName) {
|
|
479
|
-
await this.saveCampaign(
|
|
485
|
+
await this.saveCampaign(false);
|
|
480
486
|
return;
|
|
481
487
|
}
|
|
482
488
|
if (gc.tableColumn?.name !== this.molColName)
|
|
@@ -610,7 +616,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
610
616
|
|
|
611
617
|
if (this._campaign)
|
|
612
618
|
this._campaign!.savePath = this._filePath;
|
|
613
|
-
await this.saveCampaign(
|
|
619
|
+
await this.saveCampaign(true);
|
|
614
620
|
ui.empty(pathDiv);
|
|
615
621
|
const folderPath = getFolderPath();
|
|
616
622
|
link = ui.link(folderPath,
|
|
@@ -647,7 +653,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
647
653
|
};
|
|
648
654
|
}
|
|
649
655
|
|
|
650
|
-
async saveCampaign(
|
|
656
|
+
async saveCampaign(notify = true): Promise<HitDesignCampaign> {
|
|
651
657
|
const campaignId = this.campaignId!;
|
|
652
658
|
const templateName = this.template!.name;
|
|
653
659
|
const enrichedDf = this.dataFrame!;
|
|
@@ -665,7 +671,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
665
671
|
const campaign: HitDesignCampaign = {
|
|
666
672
|
name: campaignName,
|
|
667
673
|
templateName,
|
|
668
|
-
status:
|
|
674
|
+
status: this.campaign?.status ?? 'In Progress',
|
|
669
675
|
createDate: this.campaign?.createDate ?? toFormatedDateString(new Date()),
|
|
670
676
|
campaignFields: this.campaign?.campaignFields ?? this.campaignProps,
|
|
671
677
|
columnSemTypes,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as ui from 'datagrok-api/ui';
|
|
3
4
|
import * as DG from 'datagrok-api/dg';
|
|
@@ -5,10 +6,13 @@ import {u2} from '@datagrok-libraries/utils/src/u2';
|
|
|
5
6
|
import {HitDesignApp} from '../hit-design-app';
|
|
6
7
|
import {_package} from '../../package';
|
|
7
8
|
import $ from 'cash-dom';
|
|
8
|
-
import {CampaignJsonName, HitDesignCampaignIdKey, i18n} from '../consts';
|
|
9
|
+
import {CampaignGroupingType, CampaignJsonName, HitDesignCampaignIdKey, i18n} from '../consts';
|
|
9
10
|
import {HitDesignCampaign, HitDesignTemplate} from '../types';
|
|
10
11
|
import {addBreadCrumbsToRibbons, checkEditPermissions,
|
|
11
|
-
checkViewPermissions,
|
|
12
|
+
checkViewPermissions, getGroupedCampaigns, getSavedCampaignsGrouping,
|
|
13
|
+
loadCampaigns, modifyUrl, popRibbonPannels,
|
|
14
|
+
processGroupingTable,
|
|
15
|
+
setSavedCampaignsGrouping} from '../utils';
|
|
12
16
|
import {newHitDesignCampaignAccordeon} from '../accordeons/new-hit-design-campaign-accordeon';
|
|
13
17
|
import {newHitDesignTemplateAccordeon} from '../accordeons/new-hit-design-template-accordeon';
|
|
14
18
|
import {HitBaseView} from '../base-view';
|
|
@@ -17,6 +21,7 @@ import {defaultPermissions, PermissionsDialog} from '../dialogs/permissions-dial
|
|
|
17
21
|
export class HitDesignInfoView
|
|
18
22
|
<T extends HitDesignTemplate = HitDesignTemplate, K extends HitDesignApp = HitDesignApp>
|
|
19
23
|
extends HitBaseView<T, K> {
|
|
24
|
+
currentSorting: string = 'None';
|
|
20
25
|
constructor(app: K) {
|
|
21
26
|
super(app);
|
|
22
27
|
this.name = 'Hit Design';
|
|
@@ -48,6 +53,7 @@ export class HitDesignInfoView
|
|
|
48
53
|
ui.setUpdateIndicator(this.root, true);
|
|
49
54
|
try {
|
|
50
55
|
const continueCampaignsHeader = ui.h1(i18n.continueCampaigns);
|
|
56
|
+
|
|
51
57
|
const createNewCampaignHeader = ui.h1(i18n.createNewCampaignHeader, {style: {marginLeft: '10px'}});
|
|
52
58
|
const appHeader = this.getAppHeader();
|
|
53
59
|
|
|
@@ -56,10 +62,38 @@ export class HitDesignInfoView
|
|
|
56
62
|
const contentDiv = ui.div([templatesDiv, campaignAccordionDiv], 'ui-form');
|
|
57
63
|
|
|
58
64
|
const campaignsTable = await this.getCampaignsTable();
|
|
65
|
+
const tableRoot = ui.div([campaignsTable], {style: {position: 'relative'}});
|
|
66
|
+
|
|
67
|
+
const sortIcon = ui.iconFA('sort', () => {
|
|
68
|
+
const menu = DG.Menu.popup();
|
|
69
|
+
Object.values(CampaignGroupingType).forEach((i) => {
|
|
70
|
+
menu.item(i, async () => {
|
|
71
|
+
setSavedCampaignsGrouping(i as CampaignGroupingType);
|
|
72
|
+
ui.setUpdateIndicator(tableRoot, true);
|
|
73
|
+
try {
|
|
74
|
+
const t = await this.getCampaignsTable();
|
|
75
|
+
ui.setUpdateIndicator(tableRoot, false);
|
|
76
|
+
ui.empty(tableRoot);
|
|
77
|
+
tableRoot.appendChild(t);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
grok.shell.error('Failed to update campaigns table');
|
|
80
|
+
console.error(e);
|
|
81
|
+
} finally {
|
|
82
|
+
ui.setUpdateIndicator(tableRoot, false);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
menu.show({element: sortingHeader, x: 100, y: sortingHeader.offsetTop + 30});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
sortIcon.style.marginBottom = '12px';
|
|
89
|
+
sortIcon.style.marginLeft = '5px';
|
|
90
|
+
sortIcon.style.fontSize = '15px';
|
|
91
|
+
ui.tooltip.bind(sortIcon, () => `Group Campaigns. Current: ${this.currentSorting}`);
|
|
92
|
+
const sortingHeader = ui.divH([continueCampaignsHeader, sortIcon], {style: {alignItems: 'center'}});
|
|
59
93
|
$(this.root).empty();
|
|
60
94
|
this.root.appendChild(ui.div([
|
|
61
|
-
ui.divV([appHeader,
|
|
62
|
-
|
|
95
|
+
ui.divV([appHeader, sortingHeader], {style: {marginLeft: '10px'}}),
|
|
96
|
+
tableRoot,
|
|
63
97
|
createNewCampaignHeader,
|
|
64
98
|
contentDiv,
|
|
65
99
|
]));
|
|
@@ -178,7 +212,10 @@ export class HitDesignInfoView
|
|
|
178
212
|
|
|
179
213
|
private async getCampaignsTable() {
|
|
180
214
|
const campaignNamesMap = await loadCampaigns(this.app.appName, this.deletedCampaigns);
|
|
181
|
-
|
|
215
|
+
const grouppingMode = getSavedCampaignsGrouping();
|
|
216
|
+
const grouppedCampaigns = getGroupedCampaigns<HitDesignCampaign>(Object.values(campaignNamesMap), grouppingMode);
|
|
217
|
+
this.currentSorting = grouppingMode;
|
|
218
|
+
this.app.existingStatuses = Array.from(new Set(Object.values(campaignNamesMap).map((c) => c.status).filter((s) => !!s)));
|
|
182
219
|
const deleteAndShareCampaignIcons = (info: HitDesignCampaign) => {
|
|
183
220
|
const deleteIcon = ui.icons.delete(async () => {
|
|
184
221
|
ui.dialog('Delete campaign')
|
|
@@ -226,6 +263,7 @@ export class HitDesignInfoView
|
|
|
226
263
|
['Name', 'Created', 'Molecules', 'Status', '']);
|
|
227
264
|
table.style.color = 'var(--grey-5)';
|
|
228
265
|
table.style.marginLeft = '24px';
|
|
266
|
+
processGroupingTable(table, grouppedCampaigns);
|
|
229
267
|
return table;
|
|
230
268
|
}
|
|
231
269
|
|
|
@@ -239,7 +277,7 @@ export class HitDesignInfoView
|
|
|
239
277
|
this.app.dataFrame = camp.df;
|
|
240
278
|
await this.app.setTemplate(template);
|
|
241
279
|
this.app.campaignProps = camp.campaignProps;
|
|
242
|
-
await this.app.saveCampaign(
|
|
280
|
+
await this.app.saveCampaign(false);
|
|
243
281
|
if (template.layoutViewState && this.app.campaign)
|
|
244
282
|
this.app.campaign.layout = template.layoutViewState;
|
|
245
283
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import * as ui from 'datagrok-api/ui';
|
|
2
3
|
import * as grok from 'datagrok-api/grok';
|
|
3
4
|
import * as DG from 'datagrok-api/dg';
|
|
@@ -7,19 +8,54 @@ import {HitDesignTemplate} from '../types';
|
|
|
7
8
|
import {HitBaseView} from '../base-view';
|
|
8
9
|
|
|
9
10
|
export class HitDesignSubmitView extends HitBaseView<HitDesignTemplate, HitDesignApp> {
|
|
11
|
+
private statusInput: DG.InputBase<string | undefined>;
|
|
12
|
+
private statusSuggestionsMenu: DG.Menu;
|
|
13
|
+
content: HTMLDivElement;
|
|
10
14
|
constructor(app: HitDesignApp) {
|
|
11
15
|
super(app);
|
|
12
16
|
this.name = 'Submit';
|
|
17
|
+
this.statusInput = ui.input.string('Status', {value: this.app.campaign?.status, nullable: false});
|
|
18
|
+
this.statusSuggestionsMenu = DG.Menu.popup();
|
|
19
|
+
this.statusInput.root.style.marginLeft = '12px';
|
|
20
|
+
this.content = ui.div();
|
|
21
|
+
|
|
22
|
+
this.statusInput.onChanged.subscribe(() => {
|
|
23
|
+
this.statusSuggestionsMenu.clear();
|
|
24
|
+
const status = (this.statusInput.value ?? '').toLowerCase();
|
|
25
|
+
// eslint-disable-next-line max-len
|
|
26
|
+
const similarStatuses = this.app.existingStatuses.filter((s) => s?.toLowerCase()?.includes(status) && s?.toLowerCase() !== status).filter((_, i) => i < 5);
|
|
27
|
+
if (similarStatuses.length) {
|
|
28
|
+
similarStatuses.forEach((s) => {
|
|
29
|
+
this.statusSuggestionsMenu.item(s, () => {
|
|
30
|
+
this.statusInput.value = s;
|
|
31
|
+
this.statusSuggestionsMenu.root.remove();
|
|
32
|
+
this.statusInput.root.focus();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
if (this.content.parentElement?.parentElement) {
|
|
36
|
+
const xOffset = this.statusInput.root.offsetLeft + (this.statusInput.input?.offsetLeft ?? 0);
|
|
37
|
+
const yOffset = this.statusInput.root.offsetTop + this.statusInput.root.offsetHeight + this.content.parentElement.offsetTop;
|
|
38
|
+
this.statusSuggestionsMenu.show({element: this.content.parentElement.parentElement!, x: xOffset, y: yOffset});
|
|
39
|
+
}
|
|
40
|
+
} else
|
|
41
|
+
this.statusSuggestionsMenu.root.remove();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public getStatus() {
|
|
46
|
+
return this.statusInput.value ?? this.app.campaign?.status ?? 'No Status';
|
|
13
47
|
}
|
|
14
48
|
|
|
15
49
|
render(): HTMLDivElement {
|
|
16
|
-
|
|
50
|
+
this.statusInput.value = this.app.campaign?.status ?? '';
|
|
51
|
+
ui.empty(this.content);
|
|
17
52
|
|
|
18
|
-
|
|
53
|
+
this.content = ui.divV([
|
|
19
54
|
ui.h1('Summary'),
|
|
20
55
|
ui.div([ui.tableFromMap(this.app.getSummary())]),
|
|
56
|
+
this.statusInput.root,
|
|
21
57
|
]);
|
|
22
|
-
return content;
|
|
58
|
+
return this.content;
|
|
23
59
|
}
|
|
24
60
|
|
|
25
61
|
onActivated(): void {
|
|
@@ -37,8 +73,8 @@ export class HitDesignSubmitView extends HitBaseView<HitDesignTemplate, HitDesig
|
|
|
37
73
|
}
|
|
38
74
|
const filteredDf = DG.DataFrame.fromCsv(this.app.dataFrame!.toCsv({filteredRowsOnly: true}));
|
|
39
75
|
await submitFn.apply({df: filteredDf, molecules: this.app.molColName});
|
|
40
|
-
this.app.campaign
|
|
41
|
-
this.app.saveCampaign(
|
|
76
|
+
this.app.campaign!.status = this.getStatus();
|
|
77
|
+
this.app.saveCampaign();
|
|
42
78
|
grok.shell.info('Submitted successfully.');
|
|
43
79
|
}
|
|
44
80
|
}
|
package/src/app/pepti-hit-app.ts
CHANGED
|
@@ -154,7 +154,7 @@ export class PeptiHitApp extends HitDesignApp<PeptiHitTemplate> {
|
|
|
154
154
|
view?.grid && subs.push(view.grid.onCellValueEdited.subscribe(async (gc) => {
|
|
155
155
|
try {
|
|
156
156
|
if (gc.tableColumn?.name === TileCategoriesColName) {
|
|
157
|
-
await this.saveCampaign(
|
|
157
|
+
await this.saveCampaign(false);
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
160
|
if (gc.tableColumn?.name !== this.helmColName)
|
|
@@ -22,7 +22,7 @@ export class PeptiHitInfoView extends HitDesignInfoView<PeptiHitTemplate, PeptiH
|
|
|
22
22
|
this.app.dataFrame = camp.df;
|
|
23
23
|
await this.app.setTemplate(template);
|
|
24
24
|
this.app.campaignProps = camp.campaignProps;
|
|
25
|
-
await this.app.saveCampaign(
|
|
25
|
+
await this.app.saveCampaign(false);
|
|
26
26
|
if (template.layoutViewState && this.app.campaign)
|
|
27
27
|
this.app.campaign.layout = template.layoutViewState;
|
|
28
28
|
});
|
package/src/app/types.ts
CHANGED
package/src/app/utils.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as ui from 'datagrok-api/ui';
|
|
3
4
|
import * as DG from 'datagrok-api/dg';
|
|
4
5
|
import {Subscription} from 'rxjs';
|
|
5
|
-
import {CampaignJsonName, ComputeQueryMolColName} from './consts';
|
|
6
|
-
import {AppName, CampaignsType, TriagePermissions} from './types';
|
|
6
|
+
import {CampaignGroupingType, CampaignJsonName, ComputeQueryMolColName, HDCampaignsGroupingLSKey} from './consts';
|
|
7
|
+
import {AppName, CampaignsType, HitDesignCampaign, HitTriageCampaign, TriagePermissions} from './types';
|
|
7
8
|
import {_package} from '../package';
|
|
8
9
|
|
|
9
10
|
export const toFormatedDateString = (d: Date): string => {
|
|
@@ -164,3 +165,82 @@ export async function checkEditPermissions(authorId: string, permissions: Triage
|
|
|
164
165
|
export async function checkViewPermissions(authorId: string, permissions: TriagePermissions): Promise<boolean> {
|
|
165
166
|
return checkPermissions(authorId, Array.from(new Set([...permissions.view, ...permissions.edit])));
|
|
166
167
|
}
|
|
168
|
+
|
|
169
|
+
export function getLocalStorageValue<T = string>(key: string): T | null {
|
|
170
|
+
return localStorage.getItem(key) as unknown as T;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function setLocalStorageValue(key: string, value: string) {
|
|
174
|
+
localStorage.setItem(key, value as unknown as string);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function getSavedCampaignsGrouping(): CampaignGroupingType {
|
|
178
|
+
return getLocalStorageValue<CampaignGroupingType>(HDCampaignsGroupingLSKey) ?? CampaignGroupingType.None;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function setSavedCampaignsGrouping(value: CampaignGroupingType) {
|
|
182
|
+
setLocalStorageValue(HDCampaignsGroupingLSKey, value);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export const getGroupingKey = <T extends HitDesignCampaign | HitTriageCampaign = HitDesignCampaign>(grouping: CampaignGroupingType, campaign: T): string => {
|
|
186
|
+
switch (grouping) {
|
|
187
|
+
case CampaignGroupingType.Template:
|
|
188
|
+
return campaign.template?.key ?? campaign.templateName;
|
|
189
|
+
case CampaignGroupingType.Status:
|
|
190
|
+
return campaign.status;
|
|
191
|
+
default:
|
|
192
|
+
return '';
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export function getGroupedCampaigns<T extends HitDesignCampaign | HitTriageCampaign = HitDesignCampaign>(campaigns: T[], grouping: CampaignGroupingType):
|
|
197
|
+
{[key: string]: T[]} {
|
|
198
|
+
if (grouping === CampaignGroupingType.None)
|
|
199
|
+
return {'': campaigns};
|
|
200
|
+
const groupedCampaigns: {[key: string]: T[]} = {};
|
|
201
|
+
for (const campaign of campaigns) {
|
|
202
|
+
const key = getGroupingKey(grouping, campaign);
|
|
203
|
+
if (!groupedCampaigns[key])
|
|
204
|
+
groupedCampaigns[key] = [];
|
|
205
|
+
groupedCampaigns[key].push(campaign);
|
|
206
|
+
}
|
|
207
|
+
return groupedCampaigns;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function processGroupingTable<T extends HitDesignCampaign | HitTriageCampaign = HitDesignCampaign>(table: HTMLTableElement, groupedCampaigns: {[key: string]: T[]}, numCols = 6) {
|
|
211
|
+
table.classList.add('hit-design-groupped-campaigns-table');
|
|
212
|
+
const keys = Object.keys(groupedCampaigns);
|
|
213
|
+
if (keys.length < 2)
|
|
214
|
+
return table;
|
|
215
|
+
const body = table.getElementsByTagName('tbody')[0];
|
|
216
|
+
const rows = Array.from(table.getElementsByTagName('tr')).filter((row) => !row.classList.contains('header'));
|
|
217
|
+
let curRow = 0;
|
|
218
|
+
|
|
219
|
+
const setState = (expanded: boolean, start: number, end: number) => {
|
|
220
|
+
for (let i = start; i < end; i++)
|
|
221
|
+
rows[i]?.style && (rows[i].style.display = expanded ? 'table-row' : 'none');
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
for (const key of keys) {
|
|
226
|
+
const row = rows[curRow];
|
|
227
|
+
if (!row)
|
|
228
|
+
break;
|
|
229
|
+
const l = groupedCampaigns[key].length;
|
|
230
|
+
const startRow = curRow;
|
|
231
|
+
const endRow = curRow + l;
|
|
232
|
+
const acc = ui.accordion(`Hit-Design-campaigns-group-${key}`);
|
|
233
|
+
const pane = acc.addPane(key, () => {return ui.div();}, undefined, undefined, false);
|
|
234
|
+
pane.root.style.marginLeft = '-24px';
|
|
235
|
+
pane.root.onclick = () => {
|
|
236
|
+
setState(pane.expanded, startRow, endRow);
|
|
237
|
+
};
|
|
238
|
+
setState(pane.expanded, startRow, endRow);
|
|
239
|
+
const newRow = body.insertRow(0);
|
|
240
|
+
const newCell = newRow.insertCell(0);
|
|
241
|
+
newCell.appendChild(pane.root);
|
|
242
|
+
newCell.colSpan = numCols;
|
|
243
|
+
body.insertBefore(newRow, row);
|
|
244
|
+
curRow += l;
|
|
245
|
+
}
|
|
246
|
+
}
|