complexqa_frontend_core 1.23.1 → 1.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/publish/api/index.js +8 -0
  3. package/publish/api/tag/tag_color_api.js +16 -0
  4. package/publish/api/tag/tag_template_api.js +16 -0
  5. package/publish/api/tag_api.js +52 -0
  6. package/publish/api/tag_relation_api.js +57 -0
  7. package/publish/app_configuration/table/helpers/table_element_models.js +4 -0
  8. package/publish/app_configuration/table/sections/bug.js +7 -1
  9. package/publish/app_configuration/table/sections/tag.js +84 -0
  10. package/publish/app_configuration/table/sections/tag_relation.js +309 -0
  11. package/publish/app_configuration/table/sections/test_account.js +7 -1
  12. package/publish/app_configuration/table/sections/test_case.js +7 -1
  13. package/publish/app_configuration/table/sections/test_run.js +7 -1
  14. package/publish/app_configuration/table/sections/test_run_result.js +7 -1
  15. package/publish/app_configuration/table/sections/text_document.js +7 -1
  16. package/publish/app_configuration/table/table_configuration.js +4 -0
  17. package/publish/index.js +2 -0
  18. package/publish/types/core/1_familyTestDocumentation.js +9 -1
  19. package/publish/types/element_translate/typeBug.i18n.js +11 -0
  20. package/publish/types/element_translate/typeTag.i18n.js +69 -0
  21. package/publish/types/element_translate/typeTagRelation.i18n.js +74 -0
  22. package/publish/types/element_translate/typeTestAccount.i18n.js +11 -0
  23. package/publish/types/element_translate/typeTestCase.i18n.js +11 -0
  24. package/publish/types/element_translate/typeTestRun.i18n.js +11 -0
  25. package/publish/types/element_translate/typeTestRunResult.i18n.js +11 -0
  26. package/publish/types/element_translate/typeTextDocument.i18n.js +11 -0
  27. package/publish/types/family_elements/typeBug.js +0 -2
  28. package/publish/types/family_elements/typeTag.js +84 -0
  29. package/publish/types/family_elements/typeTestAccount.js +2 -0
  30. package/publish/types/family_events/familyEvent.js +2 -0
  31. package/publish/types/family_files/typeTextDocument.js +2 -0
  32. package/publish/types/family_relation/typeTagRelation.js +123 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "complexqa_frontend_core",
3
- "version": "1.23.1",
3
+ "version": "1.24.1",
4
4
  "description": "core of web ",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,6 +13,10 @@ import { TaskApi } from "./task_api.js";
13
13
  import { TestAccountApi } from "./test_account_api.js";
14
14
  import { BugApi } from "./bug_api.js";
15
15
  import { BugRelationApi } from "./bug_relation_api.js";
16
+ import { TagApi } from "./tag_api.js";
17
+ import { TagRelationApi } from "./tag_relation_api.js";
18
+ import { TagColorApi } from "./tag/tag_color_api.js";
19
+ import { TagTemplateApi } from "./tag/tag_template_api.js";
16
20
  import { ContextBrowserApi } from "./context/context_browser_api.js";
17
21
  import { ContextDeploymentTargetApi } from "./context/context_deployment_target_api.js";
18
22
  import { ContextDevicesApi } from "./context/context_devices_api.js";
@@ -166,6 +170,8 @@ export class Api
166
170
  this.test_account = new TestAccountApi(payload);
167
171
  this.bug = new BugApi(payload);
168
172
  this.bug_relation = new BugRelationApi(payload);
173
+ this.tag = new TagApi(payload);
174
+ this.tag_relation = new TagRelationApi(payload);
169
175
  this.event = new EventApi(payload);
170
176
  this.execution_context = new ExecutionContextApi(payload);
171
177
  this.browser_tools_util_execution = new BrowserToolsUtilExecutionApi(payload);
@@ -194,6 +200,8 @@ export class Api
194
200
  response.context_locale = new ContextLocaleApi(payload);
195
201
  response.context_os = new ContextOsApi(payload);
196
202
  response.context_screen_resolution = new ContextScreenResolutionApi(payload);
203
+ response.tag_color = new TagColorApi(payload);
204
+ response.tag_template = new TagTemplateApi(payload);
197
205
 
198
206
  return response;
199
207
  }
@@ -0,0 +1,16 @@
1
+ import { ApiAbstractReferenceClass } from '../api_abstract_reference_class.js';
2
+
3
+ /**
4
+ * System tag color palette (read-only).
5
+ *
6
+ * @version v.0.1 (12/08/2026)
7
+ */
8
+ export class TagColorApi extends ApiAbstractReferenceClass
9
+ {
10
+ module_prefix = 'tag_color';
11
+
12
+ constructor(options)
13
+ {
14
+ super(options);
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ import { ApiAbstractReferenceClass } from '../api_abstract_reference_class.js';
2
+
3
+ /**
4
+ * System tag templates (read-only for tenants).
5
+ *
6
+ * @version v.0.1 (12/08/2026)
7
+ */
8
+ export class TagTemplateApi extends ApiAbstractReferenceClass
9
+ {
10
+ module_prefix = 'tag_template';
11
+
12
+ constructor(options)
13
+ {
14
+ super(options);
15
+ }
16
+ }
@@ -0,0 +1,52 @@
1
+ import { ApiAbstractElementClass } from './api_abstract_element_class.js';
2
+ import { typeTag } from '../types/family_elements/typeTag.js';
3
+
4
+ /**
5
+ * CRUD / Search / clone_from_templates for tag.
6
+ *
7
+ * @version v.0.1 (12/08/2026)
8
+ */
9
+ export class TagApi extends ApiAbstractElementClass
10
+ {
11
+ module_prefix = 'tag';
12
+
13
+ constructor(options)
14
+ {
15
+ super(options);
16
+ this.set_element_class_instance(typeTag);
17
+ }
18
+
19
+
20
+ /**
21
+ * Clone system tag_templates into project catalog.
22
+ *
23
+ * @param {{ project_id: number, tag_template_ids: number[] }} payload
24
+ * @returns {Promise<{response: *, payload: *}>}
25
+ */
26
+ async clone_from_templates(payload)
27
+ {
28
+ if (!this.module_prefix)
29
+ {
30
+ throw new Error('Error #tag-clone-1152 in TagApi');
31
+ }
32
+
33
+ if (this.stage === 'development')
34
+ {
35
+ return {
36
+ response: payload,
37
+ payload : payload,
38
+ };
39
+ }
40
+
41
+ let url = `/${ this.api_prefix }/${ this.module_prefix }/clone_from_templates`;
42
+
43
+ payload = this.handle_request_payload(payload);
44
+ let response = this.api_request(url, payload);
45
+ response = this.handle_response(response);
46
+
47
+ return {
48
+ response: response,
49
+ payload : payload,
50
+ };
51
+ }
52
+ }
@@ -0,0 +1,57 @@
1
+ import { ApiAbstractElementClass } from './api_abstract_element_class.js';
2
+ import { typeTagRelation } from '../types/family_relation/typeTagRelation.js';
3
+
4
+ /**
5
+ * CRUD / Search / create_mass для tag_relation.
6
+ *
7
+ * @version v.0.2 (12/08/2026)
8
+ */
9
+ export class TagRelationApi extends ApiAbstractElementClass
10
+ {
11
+ module_prefix = 'tag_relation';
12
+
13
+ constructor(options)
14
+ {
15
+ super(options);
16
+ this.set_element_class_instance(typeTagRelation);
17
+ }
18
+
19
+
20
+ /**
21
+ * Additive mass attach: selected tags → selected elements (existing links untouched).
22
+ *
23
+ * @param {{
24
+ * project_id: number,
25
+ * element_type: string,
26
+ * element_ids: number[],
27
+ * tag_ids: number[]
28
+ * }} payload
29
+ * @returns {Promise<{response: *, payload: *}>}
30
+ */
31
+ async create_mass(payload)
32
+ {
33
+ if (!this.module_prefix)
34
+ {
35
+ throw new Error('Error #tag-relation-create-mass-1152 in TagRelationApi');
36
+ }
37
+
38
+ if (this.stage === 'development')
39
+ {
40
+ return {
41
+ response: payload,
42
+ payload : payload,
43
+ };
44
+ }
45
+
46
+ let url = `/${ this.api_prefix }/${ this.module_prefix }/create_mass`;
47
+
48
+ payload = this.handle_request_payload(payload);
49
+ let response = this.api_request(url, payload);
50
+ response = this.handle_response(response);
51
+
52
+ return {
53
+ response: response,
54
+ payload : payload,
55
+ };
56
+ }
57
+ }
@@ -3,6 +3,8 @@ import { typeProject } from '../../../types/family_elements/typeProject.js';
3
3
  import { typeTestRun } from '../../../types/family_elements/typeTestRun.js';
4
4
  import { typeBug } from '../../../types/family_elements/typeBug.js';
5
5
  import { typeBugRelation } from '../../../types/family_relation/typeBugRelation.js';
6
+ import { typeTag } from '../../../types/family_elements/typeTag.js';
7
+ import { typeTagRelation } from '../../../types/family_relation/typeTagRelation.js';
6
8
  import { typeTestRunResult } from '../../../types/family_elements/typeTestRunResult.js';
7
9
  import { typeTestAccount } from '../../../types/family_elements/typeTestAccount.js';
8
10
  import { typeExecutionContext } from '../../../types/family_context/typeExecutionContext.js';
@@ -31,6 +33,8 @@ export const TABLE_ELEMENT_MODELS = {
31
33
  test_run : typeTestRun,
32
34
  bug : typeBug,
33
35
  bug_relation : typeBugRelation,
36
+ tag : typeTag,
37
+ tag_relation : typeTagRelation,
34
38
  test_run_result: typeTestRunResult,
35
39
  test_account : typeTestAccount,
36
40
  execution_context : typeExecutionContext,
@@ -31,6 +31,12 @@ function build_bug_listing_columns(locale, { include_select = true } = {})
31
31
  columns.push(createColumn(model, locale, 'bug_type', { width: '150', is_editable: true, is_required: true }));
32
32
  columns.push(createColumn(model, locale, 'bug_resolution', { width: '150', is_editable: true, is_required: true, hide: true }));
33
33
  columns.push(createColumn(model, locale, 'assigned_to', { width: '220', is_editable: true, is_required: true }));
34
+ columns.push(createColumn(model, locale, 'tags', {
35
+ width : '220',
36
+ is_editable: false,
37
+ is_sortable: false,
38
+ is_required: false,
39
+ }));
34
40
  columns.push(createColumn(model, locale, 'created_at', { width: '170', is_required: true }));
35
41
  columns.push(createColumn(model, locale, 'completed_at', { width: '170', is_required: true, hide: true }));
36
42
  columns.push(createGoToColumn(model, locale));
@@ -96,7 +102,7 @@ export function bootstrap_bug(config)
96
102
  const locale = UserService.get_current_language();
97
103
 
98
104
  registerSection(config, 'bug', 'listing', build_bug_listing_columns(locale), {
99
- for: createListingFor([ 'project_id' ]),
105
+ for: createListingFor([ 'project_id' ], { add: [ 'tags' ] }),
100
106
  });
101
107
 
102
108
  registerSection(config, 'bug', 'todo', build_bug_todo_columns(locale), {
@@ -0,0 +1,84 @@
1
+ import { typeTag } from '../../../types/family_elements/typeTag.js';
2
+ import { UserService } from '../../../services/UserService.js';
3
+ import {
4
+ createColumn,
5
+ createGoToColumn,
6
+ createPrimaryKeyColumn,
7
+ createRowSelectColumn,
8
+ } from '../helpers/column_factory.js';
9
+ import { createListingFor, registerSection } from '../helpers/section_registry.js';
10
+ import { typeTableColumn } from '../../../types/family_service/typeTableColumn.js';
11
+
12
+ function build_tag_listing_columns(locale)
13
+ {
14
+ const model = new typeTag();
15
+
16
+ return [
17
+ createRowSelectColumn(),
18
+ createPrimaryKeyColumn(model, locale),
19
+ createColumn(model, locale, 'name', {
20
+ width : '280',
21
+ is_editable: true,
22
+ is_required: true,
23
+ is_title : true,
24
+ }),
25
+ createColumn(model, locale, 'description', { width: '420', is_editable: true }),
26
+ createColumn(model, locale, 'created_at', { width: '170' }),
27
+ createColumn(model, locale, 'updated_at', { width: '170' }),
28
+ createGoToColumn(model, locale),
29
+ ];
30
+ }
31
+
32
+
33
+ function build_tag_details_columns(locale)
34
+ {
35
+ const model = new typeTag();
36
+ const fields = [
37
+ 'tag_id',
38
+ 'project_id',
39
+ 'team_id',
40
+ 'name',
41
+ 'tag_color_id',
42
+ 'created_at',
43
+ 'updated_at',
44
+ ];
45
+
46
+ return [
47
+ ...fields.map((field, index) => createColumn(model, locale, field, {
48
+ sort_order : String(index + 1),
49
+ is_editable: [ 'name', 'tag_color_id' ].includes(field),
50
+ is_sortable: false,
51
+ is_filter : false,
52
+ })),
53
+ new typeTableColumn({
54
+ header_name: 'copy',
55
+ field : 'copy',
56
+ width : '80',
57
+ sort_order : '99',
58
+ is_sortable: false,
59
+ is_filter : false,
60
+ is_editable: false,
61
+ column_kind: 'copy',
62
+ }),
63
+ ];
64
+ }
65
+
66
+
67
+ /**
68
+ * Секции `tag`: `listing`, `details`.
69
+ *
70
+ * @param {Object} config
71
+ * @returns {void}
72
+ */
73
+ export function bootstrap_tag(config)
74
+ {
75
+ const locale = UserService.get_current_language();
76
+
77
+ registerSection(config, 'tag', 'listing', build_tag_listing_columns(locale), {
78
+ for: createListingFor([ 'project_id' ], { add: [ 'colors' ] }),
79
+ });
80
+
81
+ registerSection(config, 'tag', 'details', build_tag_details_columns(locale), {
82
+ for: createListingFor([ 'tag_id' ], { add: [ 'colors' ] }),
83
+ });
84
+ }
@@ -0,0 +1,309 @@
1
+ import { typeTagRelation } from '../../../types/family_relation/typeTagRelation.js';
2
+ import { typeTestCase } from '../../../types/family_elements/typeTestCase.js';
3
+ import { typeBug } from '../../../types/family_elements/typeBug.js';
4
+ import { typeTestRun } from '../../../types/family_elements/typeTestRun.js';
5
+ import { typeTestSuite } from '../../../types/family_elements/typeTestSuite.js';
6
+ import { typeTestAccount } from '../../../types/family_elements/typeTestAccount.js';
7
+ import { typeTestRunResult } from '../../../types/family_elements/typeTestRunResult.js';
8
+ import { typeTextDocument } from '../../../types/family_files/typeTextDocument.js';
9
+ import { typeExecutionContext } from '../../../types/family_context/typeExecutionContext.js';
10
+ import { UserService } from '../../../services/UserService.js';
11
+ import { createColumn, createGoToColumn } from '../helpers/column_factory.js';
12
+ import { get_execution_context_listing_field_specs } from '../helpers/execution_context_listing_fields.js';
13
+ import { createListingFor, registerSection } from '../helpers/section_registry.js';
14
+
15
+ /**
16
+ * Nested column helper: `element_id.<field>` with header from target element type.
17
+ *
18
+ * @param {typeTagRelation} relation_model
19
+ * @param {string} locale
20
+ * @param {string} header_element_type
21
+ * @param {string} field
22
+ * @param {object} [overrides]
23
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn}
24
+ */
25
+ function elementColumn(relation_model, locale, header_element_type, field, overrides = {})
26
+ {
27
+ return createColumn(relation_model, locale, `element_id.${ field }`, {
28
+ header_field : overrides.header_field || field,
29
+ header_element_type: overrides.header_element_type || header_element_type,
30
+ is_editable : false,
31
+ ...overrides,
32
+ });
33
+ }
34
+
35
+
36
+ /**
37
+ * Linked test cases on tag/details — same fields as test_case × listing (read-only).
38
+ *
39
+ * @param {string} locale
40
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
41
+ */
42
+ function build_tag_relation_test_cases_columns(locale)
43
+ {
44
+ const relation_model = new typeTagRelation();
45
+ const test_case_model = new typeTestCase();
46
+ const col = (field, overrides = {}) =>
47
+ elementColumn(relation_model, locale, 'test_case', field, overrides);
48
+
49
+ return [
50
+ col('test_case_id', { width: '60', default_sort: 'desc' }),
51
+ col('test_case_title', { width: '480', is_required: true, is_title: true }),
52
+ col('assigned_to', { width: '220' }),
53
+ col('test_case_time_to_execute', { width: '80', is_required: true }),
54
+ col('test_case_complexity', { width: '80', is_required: true }),
55
+ col('test_case_importance', { width: '80', is_required: true }),
56
+ col('test_case_status', { width: '150', is_required: true }),
57
+ createGoToColumn(test_case_model, locale, { field: 'element_id' }),
58
+ ];
59
+ }
60
+
61
+
62
+ /**
63
+ * Linked bugs on tag/details — same fields as bug × listing (read-only, without tags).
64
+ *
65
+ * @param {string} locale
66
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
67
+ */
68
+ function build_tag_relation_bugs_columns(locale)
69
+ {
70
+ const relation_model = new typeTagRelation();
71
+ const bug_model = new typeBug();
72
+ const col = (field, overrides = {}) =>
73
+ elementColumn(relation_model, locale, 'bug', field, overrides);
74
+
75
+ return [
76
+ col('bug_id', { width: '60', default_sort: 'desc' }),
77
+ col('bug_title', { width: '420', is_required: true, is_title: true }),
78
+ col('bug_status', { width: '150', is_required: true }),
79
+ col('bug_priority', { width: '130', is_required: true }),
80
+ col('bug_severity', { width: '130', is_required: true }),
81
+ col('bug_type', { width: '150', is_required: true }),
82
+ col('bug_resolution', { width: '150', is_required: true, is_hide: true }),
83
+ col('assigned_to', { width: '220', is_required: true }),
84
+ col('created_at', { width: '170', is_required: true }),
85
+ col('completed_at', { width: '170', is_required: true, is_hide: true }),
86
+ createGoToColumn(bug_model, locale, { field: 'element_id' }),
87
+ ];
88
+ }
89
+
90
+
91
+ /**
92
+ * Linked test runs on tag/details — same fields as test_run × listing (read-only, without tags).
93
+ *
94
+ * @param {string} locale
95
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
96
+ */
97
+ function build_tag_relation_test_runs_columns(locale)
98
+ {
99
+ const relation_model = new typeTagRelation();
100
+ const test_run_model = new typeTestRun();
101
+ const col = (field, overrides = {}) =>
102
+ elementColumn(relation_model, locale, 'test_run', field, overrides);
103
+
104
+ return [
105
+ col('test_run_id', { width: '60', default_sort: 'desc' }),
106
+ col('test_run_name', { width: '480', is_required: true, is_title: true }),
107
+ col('test_run_description', { width: '480', is_required: true }),
108
+ col('assigned_to', { width: '220', is_required: true }),
109
+ col('test_run_status', { width: '150', is_required: true }),
110
+ col('created_at', { width: '170', is_required: true }),
111
+ col('completed_at', { width: '170', is_required: true }),
112
+ createGoToColumn(test_run_model, locale, { field: 'element_id' }),
113
+ ];
114
+ }
115
+
116
+
117
+ /**
118
+ * Linked test suites on tag/details.
119
+ *
120
+ * @param {string} locale
121
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
122
+ */
123
+ function build_tag_relation_test_suites_columns(locale)
124
+ {
125
+ const relation_model = new typeTagRelation();
126
+ const test_suite_model = new typeTestSuite();
127
+ const col = (field, overrides = {}) =>
128
+ elementColumn(relation_model, locale, 'test_suite', field, overrides);
129
+
130
+ return [
131
+ col('test_suite_id', { width: '60', default_sort: 'desc' }),
132
+ col('test_suite_name', { width: '480', is_required: true, is_title: true }),
133
+ createGoToColumn(test_suite_model, locale, { field: 'element_id' }),
134
+ ];
135
+ }
136
+
137
+
138
+ /**
139
+ * Linked test accounts on tag/details — same fields as test_account × listing (read-only, without tags).
140
+ *
141
+ * @param {string} locale
142
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
143
+ */
144
+ function build_tag_relation_test_accounts_columns(locale)
145
+ {
146
+ const relation_model = new typeTagRelation();
147
+ const test_account_model = new typeTestAccount();
148
+ const col = (field, overrides = {}) =>
149
+ elementColumn(relation_model, locale, 'test_account', field, overrides);
150
+
151
+ return [
152
+ col('test_account_id', { width: '60', default_sort: 'desc' }),
153
+ col('resource', { width: '480', is_required: true, is_title: true }),
154
+ col('login', { width: '480', is_required: true }),
155
+ col('password', { width: '480' }),
156
+ col('test_account_description', { width: '480' }),
157
+ col('created_at', { width: '170' }),
158
+ col('updated_at', { width: '170' }),
159
+ createGoToColumn(test_account_model, locale, { field: 'element_id' }),
160
+ ];
161
+ }
162
+
163
+
164
+ /**
165
+ * Linked test run results on tag/details — same fields as bug_relation × test_run_results.
166
+ *
167
+ * @param {string} locale
168
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
169
+ */
170
+ function build_tag_relation_test_run_results_columns(locale)
171
+ {
172
+ const relation_model = new typeTagRelation();
173
+ const result_model = new typeTestRunResult();
174
+ const col = (field, overrides = {}) =>
175
+ elementColumn(relation_model, locale, 'test_run_result', field, overrides);
176
+
177
+ return [
178
+ col('test_run_result_id', { width: '60', default_sort: 'desc' }),
179
+ col('assigned_to', { width: '220', is_required: true }),
180
+ col('created_at', { width: '170', is_required: true }),
181
+ col('updated_at', { width: '170', is_required: true }),
182
+ col('completed_at', { width: '170', is_required: true }),
183
+ col('test_result_status', { width: '150', is_required: true }),
184
+ createGoToColumn(result_model, locale, { field: 'element_id' }),
185
+ ];
186
+ }
187
+
188
+
189
+ /**
190
+ * Linked text documents on tag/details — same fields as text_document × listing (read-only, without tags).
191
+ *
192
+ * @param {string} locale
193
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
194
+ */
195
+ function build_tag_relation_text_documents_columns(locale)
196
+ {
197
+ const relation_model = new typeTagRelation();
198
+ const text_document_model = new typeTextDocument();
199
+ const col = (field, overrides = {}) =>
200
+ elementColumn(relation_model, locale, 'text_document', field, overrides);
201
+
202
+ return [
203
+ col('text_document_id', { width: '60', default_sort: 'desc' }),
204
+ col('text_document_title', { width: '320', is_title: true }),
205
+ col('text_document_status', { width: '140' }),
206
+ col('text_document_folder_id', { width: '120' }),
207
+ col('created_at', { width: '170' }),
208
+ col('updated_at', { width: '170' }),
209
+ createGoToColumn(text_document_model, locale, { field: 'element_id' }),
210
+ ];
211
+ }
212
+
213
+
214
+ /**
215
+ * Linked execution contexts on tag/details.
216
+ *
217
+ * @param {string} locale
218
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
219
+ */
220
+ function build_tag_relation_execution_contexts_columns(locale)
221
+ {
222
+ const relation_model = new typeTagRelation();
223
+ const context_model = new typeExecutionContext();
224
+ const col = (field, overrides = {}) =>
225
+ elementColumn(relation_model, locale, 'execution_context', field, overrides);
226
+
227
+ return [
228
+ ...get_execution_context_listing_field_specs().map(({ field, overrides }) => col(field, overrides)),
229
+ createGoToColumn(context_model, locale, { field: 'element_id' }),
230
+ ];
231
+ }
232
+
233
+
234
+ /**
235
+ * Tags attached to an element (hydrate tag_id).
236
+ *
237
+ * @param {string} locale
238
+ * @returns {import('../../../types/family_service/typeTableColumn.js').typeTableColumn[]}
239
+ */
240
+ function build_element_tags_columns(locale)
241
+ {
242
+ const relation_model = new typeTagRelation();
243
+ const columns = [];
244
+
245
+ const tagColumn = (field, overrides = {}) => createColumn(relation_model, locale, `tag_id.${ field }`, {
246
+ header_field : field,
247
+ header_element_type: 'tag',
248
+ is_editable : false,
249
+ ...overrides,
250
+ });
251
+
252
+ columns.push(tagColumn('tag_id', { width: '60', default_sort: 'desc' }));
253
+ columns.push(tagColumn('name', {
254
+ width : '280',
255
+ is_required: true,
256
+ is_title : true,
257
+ }));
258
+ columns.push(tagColumn('tag_color_id', { width: '120' }));
259
+
260
+ return columns;
261
+ }
262
+
263
+
264
+ /**
265
+ * Секции `tag_relation` для tag/details tabs и attach на элементах.
266
+ *
267
+ * @param {Object} config
268
+ * @returns {void}
269
+ */
270
+ export function bootstrap_tag_relation(config)
271
+ {
272
+ const locale = UserService.get_current_language();
273
+
274
+ registerSection(config, 'tag_relation', 'test_cases', build_tag_relation_test_cases_columns(locale), {
275
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
276
+ });
277
+
278
+ registerSection(config, 'tag_relation', 'bugs', build_tag_relation_bugs_columns(locale), {
279
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
280
+ });
281
+
282
+ registerSection(config, 'tag_relation', 'test_runs', build_tag_relation_test_runs_columns(locale), {
283
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
284
+ });
285
+
286
+ registerSection(config, 'tag_relation', 'test_suites', build_tag_relation_test_suites_columns(locale), {
287
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
288
+ });
289
+
290
+ registerSection(config, 'tag_relation', 'test_accounts', build_tag_relation_test_accounts_columns(locale), {
291
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
292
+ });
293
+
294
+ registerSection(config, 'tag_relation', 'test_run_results', build_tag_relation_test_run_results_columns(locale), {
295
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
296
+ });
297
+
298
+ registerSection(config, 'tag_relation', 'text_documents', build_tag_relation_text_documents_columns(locale), {
299
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
300
+ });
301
+
302
+ registerSection(config, 'tag_relation', 'execution_contexts', build_tag_relation_execution_contexts_columns(locale), {
303
+ for: createListingFor([ 'tag_id', 'element_type' ], { add: [ 'elements' ] }),
304
+ });
305
+
306
+ registerSection(config, 'tag_relation', 'element_tags', build_element_tags_columns(locale), {
307
+ for: createListingFor([ 'element_type', 'element_id' ], { add: [ 'tags' ] }),
308
+ });
309
+ }
@@ -12,6 +12,12 @@ function build_test_account_listing_columns(locale)
12
12
  createPrimaryKeyColumn(model, locale),
13
13
  createColumn(model, locale, 'resource', { width: '480', is_editable: true, is_required: true }),
14
14
  createColumn(model, locale, 'login', { width: '480', is_editable: true, is_required: true }),
15
+ createColumn(model, locale, 'tags', {
16
+ width : '220',
17
+ is_editable: false,
18
+ is_sortable: false,
19
+ is_required: false,
20
+ }),
15
21
  createColumn(model, locale, 'password', { width: '480', is_editable: true }),
16
22
  createColumn(model, locale, 'test_account_description', { width: '480', is_editable: true }),
17
23
  createColumn(model, locale, 'created_at', { width: '170' }),
@@ -67,7 +73,7 @@ export function bootstrap_test_account(config)
67
73
  const locale = UserService.get_current_language();
68
74
 
69
75
  registerSection(config, 'test_account', 'listing', build_test_account_listing_columns(locale), {
70
- for: createListingFor([ 'project_id' ]),
76
+ for: createListingFor([ 'project_id' ], { add: [ 'tags' ] }),
71
77
  });
72
78
 
73
79
  registerSection(config, 'test_account', 'details', build_test_account_details_columns(locale));
@@ -21,6 +21,12 @@ function build_test_case_listing_columns(locale, { include_select = true, includ
21
21
  is_title : true,
22
22
  }));
23
23
  columns.push(createColumn(model, locale, 'assigned_to', { width: '220', is_editable: true }));
24
+ columns.push(createColumn(model, locale, 'tags', {
25
+ width : '220',
26
+ is_editable: false,
27
+ is_sortable: false,
28
+ is_required: false,
29
+ }));
24
30
  columns.push(createColumn(model, locale, 'test_case_time_to_execute', { width: '80', is_editable: true, is_required: true }));
25
31
  columns.push(createColumn(model, locale, 'test_case_complexity', { width: '80', is_editable: true, is_required: true }));
26
32
  columns.push(createColumn(model, locale, 'test_case_importance', { width: '80', is_editable: true, is_required: true }));
@@ -59,7 +65,7 @@ export function bootstrap_test_case(config)
59
65
  const locale = UserService.get_current_language();
60
66
 
61
67
  registerSection(config, 'test_case', 'listing', build_test_case_listing_columns(locale), {
62
- for: createListingFor([ 'project_id', 'test_suite_id' ]),
68
+ for: createListingFor([ 'project_id', 'test_suite_id' ], { add: [ 'tags' ] }),
63
69
  });
64
70
 
65
71
  registerSection(config, 'test_case', 'todo', build_test_case_todo_columns(locale), {