complexqa_frontend_core 1.26.1 → 1.27.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.
- package/package.json +1 -1
- package/publish/api/custom_property/custom_property_template_api.js +16 -0
- package/publish/api/custom_property_api.js +54 -0
- package/publish/api/custom_property_option_api.js +17 -0
- package/publish/api/custom_property_value_api.js +82 -0
- package/publish/api/index.js +8 -0
- package/publish/app_configuration/table/helpers/table_element_models.js +4 -0
- package/publish/app_configuration/table/sections/custom_property.js +96 -0
- package/publish/app_configuration/table/table_configuration.js +2 -0
- package/publish/index.js +2 -0
- package/publish/services/UserService.js +9 -0
- package/publish/types/core/1_familyTestDocumentation.js +8 -0
- package/publish/types/element_translate/typeCustomProperty.i18n.js +85 -0
- package/publish/types/element_translate/typeCustomPropertyValue.i18n.js +65 -0
- package/publish/types/element_translate/typeTeamTotalStatistic.i18n.js +11 -0
- package/publish/types/family_elements/typeCustomProperty.js +138 -0
- package/publish/types/family_events/familyEvent.js +2 -0
- package/publish/types/family_relation/typeCustomPropertyValue.js +117 -0
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ApiAbstractReferenceClass } from '../api_abstract_reference_class.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* System custom property templates (read-only for tenants).
|
|
5
|
+
*
|
|
6
|
+
* @version v.0.1 (15/08/2026)
|
|
7
|
+
*/
|
|
8
|
+
export class CustomPropertyTemplateApi extends ApiAbstractReferenceClass
|
|
9
|
+
{
|
|
10
|
+
module_prefix = 'custom_property_template';
|
|
11
|
+
|
|
12
|
+
constructor(options)
|
|
13
|
+
{
|
|
14
|
+
super(options);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ApiAbstractElementClass } from './api_abstract_element_class.js';
|
|
2
|
+
import { typeCustomProperty } from '../types/family_elements/typeCustomProperty.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CRUD / Search / clone_from_templates for custom_property catalog.
|
|
6
|
+
* Create / update / delete / clone require ADMINISTRATOR on the server.
|
|
7
|
+
*
|
|
8
|
+
* @version v.0.1 (15/08/2026)
|
|
9
|
+
*/
|
|
10
|
+
export class CustomPropertyApi extends ApiAbstractElementClass
|
|
11
|
+
{
|
|
12
|
+
module_prefix = 'custom_property';
|
|
13
|
+
|
|
14
|
+
constructor(options)
|
|
15
|
+
{
|
|
16
|
+
super(options);
|
|
17
|
+
this.set_element_class_instance(typeCustomProperty);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Clone system templates into the team catalog for one element_type.
|
|
23
|
+
* Server: ADMINISTRATOR only.
|
|
24
|
+
*
|
|
25
|
+
* @param {{ element_type: string, custom_property_template_ids: number[] }} payload
|
|
26
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
27
|
+
*/
|
|
28
|
+
async clone_from_templates(payload)
|
|
29
|
+
{
|
|
30
|
+
if (!this.module_prefix)
|
|
31
|
+
{
|
|
32
|
+
throw new Error('Error #custom-property-clone-1152 in CustomPropertyApi');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (this.stage === 'development')
|
|
36
|
+
{
|
|
37
|
+
return {
|
|
38
|
+
response: payload,
|
|
39
|
+
payload : payload,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let url = `/${ this.api_prefix }/${ this.module_prefix }/clone_from_templates`;
|
|
44
|
+
|
|
45
|
+
payload = this.handle_request_payload(payload);
|
|
46
|
+
let response = this.api_request(url, payload);
|
|
47
|
+
response = this.handle_response(response);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
response: response,
|
|
51
|
+
payload : payload,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ApiAbstractElementClass } from './api_abstract_element_class.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for DROPDOWN / MULTISELECT catalog slots.
|
|
5
|
+
* Mutations require ADMINISTRATOR on the server.
|
|
6
|
+
*
|
|
7
|
+
* @version v.0.1 (15/08/2026)
|
|
8
|
+
*/
|
|
9
|
+
export class CustomPropertyOptionApi extends ApiAbstractElementClass
|
|
10
|
+
{
|
|
11
|
+
module_prefix = 'custom_property_option';
|
|
12
|
+
|
|
13
|
+
constructor(options)
|
|
14
|
+
{
|
|
15
|
+
super(options);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ApiAbstractElementClass } from './api_abstract_element_class.js';
|
|
2
|
+
import { typeCustomPropertyValue } from '../types/family_relation/typeCustomPropertyValue.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Explicit value writers: set / add / delete. Not a union create().
|
|
6
|
+
* READONLY → api_code = -501. MEMBER and ADMINISTRATOR may write.
|
|
7
|
+
*
|
|
8
|
+
* @version v.0.1 (15/08/2026)
|
|
9
|
+
*/
|
|
10
|
+
export class CustomPropertyValueApi extends ApiAbstractElementClass
|
|
11
|
+
{
|
|
12
|
+
module_prefix = 'custom_property_value';
|
|
13
|
+
|
|
14
|
+
constructor(options)
|
|
15
|
+
{
|
|
16
|
+
super(options);
|
|
17
|
+
this.set_element_class_instance(typeCustomPropertyValue);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} payload
|
|
23
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
24
|
+
*/
|
|
25
|
+
async set(payload)
|
|
26
|
+
{
|
|
27
|
+
return this.#process('set', payload);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {object} payload
|
|
33
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
34
|
+
*/
|
|
35
|
+
async add(payload)
|
|
36
|
+
{
|
|
37
|
+
return this.#process('add', payload);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {object} payload
|
|
43
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
44
|
+
*/
|
|
45
|
+
async delete(payload)
|
|
46
|
+
{
|
|
47
|
+
return this.#process('delete', payload);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {string} action
|
|
53
|
+
* @param {object} payload
|
|
54
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
55
|
+
*/
|
|
56
|
+
async #process(action, payload)
|
|
57
|
+
{
|
|
58
|
+
if (!this.module_prefix)
|
|
59
|
+
{
|
|
60
|
+
throw new Error('Error #custom-property-value-1152 in CustomPropertyValueApi');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.stage === 'development')
|
|
64
|
+
{
|
|
65
|
+
return {
|
|
66
|
+
response: payload,
|
|
67
|
+
payload : payload,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let url = `/${ this.api_prefix }/${ this.module_prefix }/${ action }`;
|
|
72
|
+
|
|
73
|
+
payload = this.handle_request_payload(payload);
|
|
74
|
+
let response = this.api_request(url, payload);
|
|
75
|
+
response = this.handle_response(response);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
response: response,
|
|
79
|
+
payload : payload,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
package/publish/api/index.js
CHANGED
|
@@ -16,8 +16,12 @@ import { BugRelationApi } from "./bug_relation_api.js";
|
|
|
16
16
|
import { AttachmentApi } from "./attachment_api.js";
|
|
17
17
|
import { TagApi } from "./tag_api.js";
|
|
18
18
|
import { TagRelationApi } from "./tag_relation_api.js";
|
|
19
|
+
import { CustomPropertyApi } from "./custom_property_api.js";
|
|
20
|
+
import { CustomPropertyValueApi } from "./custom_property_value_api.js";
|
|
21
|
+
import { CustomPropertyOptionApi } from "./custom_property_option_api.js";
|
|
19
22
|
import { TagColorApi } from "./tag/tag_color_api.js";
|
|
20
23
|
import { TagTemplateApi } from "./tag/tag_template_api.js";
|
|
24
|
+
import { CustomPropertyTemplateApi } from "./custom_property/custom_property_template_api.js";
|
|
21
25
|
import { ContextBrowserApi } from "./context/context_browser_api.js";
|
|
22
26
|
import { ContextDeploymentTargetApi } from "./context/context_deployment_target_api.js";
|
|
23
27
|
import { ContextDevicesApi } from "./context/context_devices_api.js";
|
|
@@ -176,6 +180,9 @@ export class Api
|
|
|
176
180
|
this.attachment = new AttachmentApi(payload);
|
|
177
181
|
this.tag = new TagApi(payload);
|
|
178
182
|
this.tag_relation = new TagRelationApi(payload);
|
|
183
|
+
this.custom_property = new CustomPropertyApi(payload);
|
|
184
|
+
this.custom_property_value = new CustomPropertyValueApi(payload);
|
|
185
|
+
this.custom_property_option = new CustomPropertyOptionApi(payload);
|
|
179
186
|
this.event = new EventApi(payload);
|
|
180
187
|
this.execution_context = new ExecutionContextApi(payload);
|
|
181
188
|
this.browser_tools_util_execution = new BrowserToolsUtilExecutionApi(payload);
|
|
@@ -208,6 +215,7 @@ export class Api
|
|
|
208
215
|
response.context_screen_resolution = new ContextScreenResolutionApi(payload);
|
|
209
216
|
response.tag_color = new TagColorApi(payload);
|
|
210
217
|
response.tag_template = new TagTemplateApi(payload);
|
|
218
|
+
response.custom_property_template = new CustomPropertyTemplateApi(payload);
|
|
211
219
|
|
|
212
220
|
return response;
|
|
213
221
|
}
|
|
@@ -4,8 +4,10 @@ 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
6
|
import { typeTag } from '../../../types/family_elements/typeTag.js';
|
|
7
|
+
import { typeCustomProperty } from '../../../types/family_elements/typeCustomProperty.js';
|
|
7
8
|
import { typeAttachment } from '../../../types/family_elements/typeAttachment.js';
|
|
8
9
|
import { typeTagRelation } from '../../../types/family_relation/typeTagRelation.js';
|
|
10
|
+
import { typeCustomPropertyValue } from '../../../types/family_relation/typeCustomPropertyValue.js';
|
|
9
11
|
import { typeTestRunResult } from '../../../types/family_elements/typeTestRunResult.js';
|
|
10
12
|
import { typeTestAccount } from '../../../types/family_elements/typeTestAccount.js';
|
|
11
13
|
import { typeExecutionContext } from '../../../types/family_context/typeExecutionContext.js';
|
|
@@ -39,6 +41,8 @@ export const TABLE_ELEMENT_MODELS = {
|
|
|
39
41
|
attachment : typeAttachment,
|
|
40
42
|
tag : typeTag,
|
|
41
43
|
tag_relation : typeTagRelation,
|
|
44
|
+
custom_property: typeCustomProperty,
|
|
45
|
+
custom_property_value: typeCustomPropertyValue,
|
|
42
46
|
test_run_result: typeTestRunResult,
|
|
43
47
|
test_account : typeTestAccount,
|
|
44
48
|
execution_context : typeExecutionContext,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { typeCustomProperty } from '../../../types/family_elements/typeCustomProperty.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_custom_property_listing_columns(locale)
|
|
13
|
+
{
|
|
14
|
+
const model = new typeCustomProperty();
|
|
15
|
+
|
|
16
|
+
return [
|
|
17
|
+
createRowSelectColumn(),
|
|
18
|
+
createPrimaryKeyColumn(model, locale),
|
|
19
|
+
createColumn(model, locale, 'name', {
|
|
20
|
+
width : '240',
|
|
21
|
+
is_editable: true,
|
|
22
|
+
is_required: true,
|
|
23
|
+
is_title : true,
|
|
24
|
+
}),
|
|
25
|
+
createColumn(model, locale, 'property_key', { width: '180' }),
|
|
26
|
+
createColumn(model, locale, 'element_type', { width: '140' }),
|
|
27
|
+
createColumn(model, locale, 'value_type', { width: '150' }),
|
|
28
|
+
createColumn(model, locale, 'cardinality', { width: '140', is_editable: true }),
|
|
29
|
+
createColumn(model, locale, 'is_enabled', { width: '110', is_editable: true }),
|
|
30
|
+
createColumn(model, locale, 'is_required', { width: '110', is_editable: true }),
|
|
31
|
+
createColumn(model, locale, 'is_searchable', { width: '120', is_editable: true }),
|
|
32
|
+
createColumn(model, locale, 'created_at', { width: '170' }),
|
|
33
|
+
createColumn(model, locale, 'updated_at', { width: '170' }),
|
|
34
|
+
createGoToColumn(model, locale),
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
function build_custom_property_details_columns(locale)
|
|
40
|
+
{
|
|
41
|
+
const model = new typeCustomProperty();
|
|
42
|
+
const fields = [
|
|
43
|
+
'custom_property_id',
|
|
44
|
+
'team_id',
|
|
45
|
+
'element_type',
|
|
46
|
+
'property_key',
|
|
47
|
+
'value_type',
|
|
48
|
+
'cardinality',
|
|
49
|
+
'name',
|
|
50
|
+
'is_required',
|
|
51
|
+
'is_enabled',
|
|
52
|
+
'is_searchable',
|
|
53
|
+
'sort_order',
|
|
54
|
+
'created_at',
|
|
55
|
+
'updated_at',
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
return [
|
|
59
|
+
...fields.map((field, index) => createColumn(model, locale, field, {
|
|
60
|
+
sort_order : String(index + 1),
|
|
61
|
+
is_editable: [ 'name', 'cardinality', 'is_required', 'is_enabled', 'is_searchable', 'sort_order' ].includes(field),
|
|
62
|
+
is_sortable: false,
|
|
63
|
+
is_filter : false,
|
|
64
|
+
})),
|
|
65
|
+
new typeTableColumn({
|
|
66
|
+
header_name: 'copy',
|
|
67
|
+
field : 'copy',
|
|
68
|
+
width : '80',
|
|
69
|
+
sort_order : '99',
|
|
70
|
+
is_sortable: false,
|
|
71
|
+
is_filter : false,
|
|
72
|
+
is_editable: false,
|
|
73
|
+
column_kind: 'copy',
|
|
74
|
+
}),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Секции `custom_property`: `listing`, `details`.
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} config
|
|
83
|
+
* @returns {void}
|
|
84
|
+
*/
|
|
85
|
+
export function bootstrap_custom_property(config)
|
|
86
|
+
{
|
|
87
|
+
const locale = UserService.get_current_language();
|
|
88
|
+
|
|
89
|
+
registerSection(config, 'custom_property', 'listing', build_custom_property_listing_columns(locale), {
|
|
90
|
+
for: createListingFor([], { add: [ 'options' ] }),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
registerSection(config, 'custom_property', 'details', build_custom_property_details_columns(locale), {
|
|
94
|
+
for: createListingFor([ 'custom_property_id' ], { add: [ 'options' ] }),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -3,6 +3,7 @@ import { bootstrap_bug } from './sections/bug.js';
|
|
|
3
3
|
import { bootstrap_bug_relation } from './sections/bug_relation.js';
|
|
4
4
|
import { bootstrap_tag } from './sections/tag.js';
|
|
5
5
|
import { bootstrap_tag_relation } from './sections/tag_relation.js';
|
|
6
|
+
import { bootstrap_custom_property } from './sections/custom_property.js';
|
|
6
7
|
import { bootstrap_test_case } from './sections/test_case.js';
|
|
7
8
|
import { bootstrap_test_case_step } from './sections/test_case_step.js';
|
|
8
9
|
import { bootstrap_test_run } from './sections/test_run.js';
|
|
@@ -117,6 +118,7 @@ export class TableConfiguration extends abstractAppConfiguration
|
|
|
117
118
|
bootstrap_bug_relation(this.config);
|
|
118
119
|
bootstrap_tag(this.config);
|
|
119
120
|
bootstrap_tag_relation(this.config);
|
|
121
|
+
bootstrap_custom_property(this.config);
|
|
120
122
|
bootstrap_test_case(this.config);
|
|
121
123
|
bootstrap_test_case_step(this.config);
|
|
122
124
|
bootstrap_test_run(this.config);
|
package/publish/index.js
CHANGED
|
@@ -17,11 +17,13 @@ export { typeTestRunResult } from './types/family_elements/typeTestRunResult.js'
|
|
|
17
17
|
export { typeTestAccount } from './types/family_elements/typeTestAccount.js'
|
|
18
18
|
export {typeBug} from './types/family_elements/typeBug.js';
|
|
19
19
|
export { typeTag } from './types/family_elements/typeTag.js';
|
|
20
|
+
export { typeCustomProperty } from './types/family_elements/typeCustomProperty.js';
|
|
20
21
|
export { typeAttachment } from './types/family_elements/typeAttachment.js';
|
|
21
22
|
|
|
22
23
|
export { familyRelation } from './types/core/1_familyRelation.js';
|
|
23
24
|
export { typeBugRelation } from './types/family_relation/typeBugRelation.js';
|
|
24
25
|
export { typeTagRelation } from './types/family_relation/typeTagRelation.js';
|
|
26
|
+
export { typeCustomPropertyValue } from './types/family_relation/typeCustomPropertyValue.js';
|
|
25
27
|
|
|
26
28
|
export { familyContext } from './types/core/1_familyContext.js';
|
|
27
29
|
export { typeBrowserContext } from './types/family_context/typeBrowserContext.js';
|
|
@@ -44,6 +44,15 @@ export class UserService extends abstractService
|
|
|
44
44
|
static current_team_slug = false;
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* member_role of the current user in current_team_id: ADMINISTRATOR | MEMBER | READONLY.
|
|
49
|
+
* SPA bootstrap: auth/common_page.blade.php from team_members join in get_user_teams().
|
|
50
|
+
*
|
|
51
|
+
* @type {false|string}
|
|
52
|
+
*/
|
|
53
|
+
static current_member_role = false;
|
|
54
|
+
|
|
55
|
+
|
|
47
56
|
/**
|
|
48
57
|
*
|
|
49
58
|
* @type {false|array<typeTeam>}
|
|
@@ -16,6 +16,14 @@ export class familyTestDocumentation extends familyGeneralElement
|
|
|
16
16
|
*/
|
|
17
17
|
tags;
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Hydrated on element search via options.add: ['custom_properties'].
|
|
21
|
+
* Catalog entities leave this unset.
|
|
22
|
+
*
|
|
23
|
+
* @type {Array<object>|undefined}
|
|
24
|
+
*/
|
|
25
|
+
custom_properties;
|
|
26
|
+
|
|
19
27
|
|
|
20
28
|
/**
|
|
21
29
|
*
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Переводы для typeCustomProperty.
|
|
3
|
+
*
|
|
4
|
+
* @see typeCustomProperty
|
|
5
|
+
*/
|
|
6
|
+
export const typeCustomProperty_i18n = {
|
|
7
|
+
attribute_name_translate_matrix: {
|
|
8
|
+
en: {
|
|
9
|
+
custom_property_id: 'ID',
|
|
10
|
+
team_id : 'Team',
|
|
11
|
+
element_type : 'Element type',
|
|
12
|
+
property_key : 'Key',
|
|
13
|
+
value_type : 'Value type',
|
|
14
|
+
cardinality : 'Cardinality',
|
|
15
|
+
name : 'Name',
|
|
16
|
+
description : 'Description',
|
|
17
|
+
is_required : 'Required',
|
|
18
|
+
is_enabled : 'Enabled',
|
|
19
|
+
is_searchable : 'Searchable',
|
|
20
|
+
sort_order : 'Sort order',
|
|
21
|
+
created_at : 'Created',
|
|
22
|
+
updated_at : 'Updated',
|
|
23
|
+
},
|
|
24
|
+
ru: {
|
|
25
|
+
custom_property_id: 'ID',
|
|
26
|
+
team_id : 'Команда',
|
|
27
|
+
element_type : 'Тип элемента',
|
|
28
|
+
property_key : 'Ключ',
|
|
29
|
+
value_type : 'Тип значения',
|
|
30
|
+
cardinality : 'Кардинальность',
|
|
31
|
+
name : 'Название',
|
|
32
|
+
description : 'Описание',
|
|
33
|
+
is_required : 'Обязательное',
|
|
34
|
+
is_enabled : 'Включено',
|
|
35
|
+
is_searchable : 'Искать',
|
|
36
|
+
sort_order : 'Порядок',
|
|
37
|
+
created_at : 'Создано',
|
|
38
|
+
updated_at : 'Обновлено',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
enum_value_translate_matrix: {
|
|
42
|
+
en: {
|
|
43
|
+
element_type: {
|
|
44
|
+
test_case: 'Test case',
|
|
45
|
+
bug : 'Bug',
|
|
46
|
+
},
|
|
47
|
+
value_type: {
|
|
48
|
+
STRING : 'String',
|
|
49
|
+
TEXT : 'Text',
|
|
50
|
+
INTEGER : 'Integer',
|
|
51
|
+
DATE : 'Date',
|
|
52
|
+
URL : 'URL',
|
|
53
|
+
USER : 'User',
|
|
54
|
+
DROPDOWN : 'Dropdown',
|
|
55
|
+
CHECKBOX : 'Checkbox',
|
|
56
|
+
MULTISELECT: 'Multi-select',
|
|
57
|
+
},
|
|
58
|
+
cardinality: {
|
|
59
|
+
SINGLE : 'Single',
|
|
60
|
+
MULTIPLE: 'Multiple',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
ru: {
|
|
64
|
+
element_type: {
|
|
65
|
+
test_case: 'Тест-кейс',
|
|
66
|
+
bug : 'Ошибка',
|
|
67
|
+
},
|
|
68
|
+
value_type: {
|
|
69
|
+
STRING : 'Строка',
|
|
70
|
+
TEXT : 'Текст',
|
|
71
|
+
INTEGER : 'Целое',
|
|
72
|
+
DATE : 'Дата',
|
|
73
|
+
URL : 'URL',
|
|
74
|
+
USER : 'Пользователь',
|
|
75
|
+
DROPDOWN : 'Список',
|
|
76
|
+
CHECKBOX : 'Флажок',
|
|
77
|
+
MULTISELECT: 'Множественный выбор',
|
|
78
|
+
},
|
|
79
|
+
cardinality: {
|
|
80
|
+
SINGLE : 'Одно значение',
|
|
81
|
+
MULTIPLE: 'Несколько значений',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Переводы для typeCustomPropertyValue.
|
|
3
|
+
*
|
|
4
|
+
* @see typeCustomPropertyValue
|
|
5
|
+
*/
|
|
6
|
+
export const typeCustomPropertyValue_i18n = {
|
|
7
|
+
attribute_name_translate_matrix: {
|
|
8
|
+
en: {
|
|
9
|
+
custom_property_value_id: 'ID',
|
|
10
|
+
custom_property_id : 'Custom property',
|
|
11
|
+
team_id : 'Team',
|
|
12
|
+
project_id : 'Project',
|
|
13
|
+
element_type : 'Element type',
|
|
14
|
+
element_id : 'Element',
|
|
15
|
+
sort_order : 'Sort order',
|
|
16
|
+
user_id : 'User',
|
|
17
|
+
created_at : 'Created',
|
|
18
|
+
updated_at : 'Updated',
|
|
19
|
+
string_value : 'String',
|
|
20
|
+
text_value : 'Text',
|
|
21
|
+
integer_value : 'Integer',
|
|
22
|
+
date_value : 'Date',
|
|
23
|
+
url : 'URL',
|
|
24
|
+
url_caption : 'Caption',
|
|
25
|
+
user_id_value : 'User',
|
|
26
|
+
checkbox_value : 'Checked',
|
|
27
|
+
option_id : 'Option',
|
|
28
|
+
},
|
|
29
|
+
ru: {
|
|
30
|
+
custom_property_value_id: 'ID',
|
|
31
|
+
custom_property_id : 'Свойство',
|
|
32
|
+
team_id : 'Команда',
|
|
33
|
+
project_id : 'Проект',
|
|
34
|
+
element_type : 'Тип элемента',
|
|
35
|
+
element_id : 'Элемент',
|
|
36
|
+
sort_order : 'Порядок',
|
|
37
|
+
user_id : 'Пользователь',
|
|
38
|
+
created_at : 'Создано',
|
|
39
|
+
updated_at : 'Обновлено',
|
|
40
|
+
string_value : 'Строка',
|
|
41
|
+
text_value : 'Текст',
|
|
42
|
+
integer_value : 'Целое',
|
|
43
|
+
date_value : 'Дата',
|
|
44
|
+
url : 'URL',
|
|
45
|
+
url_caption : 'Подпись',
|
|
46
|
+
user_id_value : 'Пользователь',
|
|
47
|
+
checkbox_value : 'Флажок',
|
|
48
|
+
option_id : 'Опция',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
enum_value_translate_matrix: {
|
|
52
|
+
en: {
|
|
53
|
+
element_type: {
|
|
54
|
+
test_case: 'Test case',
|
|
55
|
+
bug : 'Bug',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
ru: {
|
|
59
|
+
element_type: {
|
|
60
|
+
test_case: 'Тест-кейс',
|
|
61
|
+
bug : 'Ошибка',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
@@ -16,6 +16,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
16
16
|
bug : 'Bugs',
|
|
17
17
|
bug_relation : 'Bug relations',
|
|
18
18
|
tag : 'Tags',
|
|
19
|
+
custom_property : 'Custom properties',
|
|
19
20
|
execution_context : 'Execution contexts',
|
|
20
21
|
task : 'Tasks',
|
|
21
22
|
team_member : 'Team members',
|
|
@@ -37,6 +38,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
37
38
|
bug : 'Баги',
|
|
38
39
|
bug_relation : 'Связи багов',
|
|
39
40
|
tag : 'Теги',
|
|
41
|
+
custom_property : 'Пользовательские свойства',
|
|
40
42
|
execution_context : 'Контексты выполнения',
|
|
41
43
|
task : 'Задачи',
|
|
42
44
|
team_member : 'Участники команды',
|
|
@@ -58,6 +60,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
58
60
|
bug : 'Fehler',
|
|
59
61
|
bug_relation : 'Fehlerverknüpfung',
|
|
60
62
|
tag : 'Tag',
|
|
63
|
+
custom_property : 'Benutzerdefinierte Eigenschaften',
|
|
61
64
|
execution_context : 'Ausführungskontext',
|
|
62
65
|
task : 'Aufgabe',
|
|
63
66
|
team_member : 'Teammitglied',
|
|
@@ -79,6 +82,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
79
82
|
bug : 'خطأ',
|
|
80
83
|
bug_relation : 'علاقة الخطأ',
|
|
81
84
|
tag : 'وسم',
|
|
85
|
+
custom_property : 'خصائص مخصصة',
|
|
82
86
|
execution_context : 'سياق التنفيذ',
|
|
83
87
|
task : 'مهمة',
|
|
84
88
|
team_member : 'عضو الفريق',
|
|
@@ -100,6 +104,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
100
104
|
bug : 'Error',
|
|
101
105
|
bug_relation : 'Relación de error',
|
|
102
106
|
tag : 'Etiqueta',
|
|
107
|
+
custom_property : 'Propiedades personalizadas',
|
|
103
108
|
execution_context : 'Contexto de ejecución',
|
|
104
109
|
task : 'Tarea',
|
|
105
110
|
team_member : 'Miembro del equipo',
|
|
@@ -121,6 +126,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
121
126
|
bug : 'Bug',
|
|
122
127
|
bug_relation : 'Relation de bug',
|
|
123
128
|
tag : 'Tag',
|
|
129
|
+
custom_property : 'Propriétés personnalisées',
|
|
124
130
|
execution_context : 'Contexte d\'exécution',
|
|
125
131
|
task : 'Tâche',
|
|
126
132
|
team_member : 'Membre de l\'équipe',
|
|
@@ -142,6 +148,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
142
148
|
bug : 'Սխալ',
|
|
143
149
|
bug_relation : 'Սխալի կապ',
|
|
144
150
|
tag : 'Պիտակ',
|
|
151
|
+
custom_property : 'Հատուկ հատկություններ',
|
|
145
152
|
execution_context : 'Կատարման համատեքստ',
|
|
146
153
|
task : 'Առաջադրանք',
|
|
147
154
|
team_member : 'Թիմի անդամ',
|
|
@@ -163,6 +170,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
163
170
|
bug : 'Bug',
|
|
164
171
|
bug_relation : 'Relazione bug',
|
|
165
172
|
tag : 'Tag',
|
|
173
|
+
custom_property : 'Proprietà personalizzate',
|
|
166
174
|
execution_context : 'Contesto di esecuzione',
|
|
167
175
|
task : 'Attività',
|
|
168
176
|
team_member : 'Membro del team',
|
|
@@ -184,6 +192,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
184
192
|
bug : 'Błąd',
|
|
185
193
|
bug_relation : 'Powiązanie błędu',
|
|
186
194
|
tag : 'Tag',
|
|
195
|
+
custom_property : 'Właściwości niestandardowe',
|
|
187
196
|
execution_context : 'Kontekst wykonania',
|
|
188
197
|
task : 'Zadanie',
|
|
189
198
|
team_member : 'Członek zespołu',
|
|
@@ -205,6 +214,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
205
214
|
bug : 'Bug',
|
|
206
215
|
bug_relation : 'Relação de bug',
|
|
207
216
|
tag : 'Tag',
|
|
217
|
+
custom_property : 'Propriedades personalizadas',
|
|
208
218
|
execution_context : 'Contexto de execução',
|
|
209
219
|
task : 'Tarefa',
|
|
210
220
|
team_member : 'Membro da equipa',
|
|
@@ -226,6 +236,7 @@ export const typeTeamTotalStatistic_i18n = {
|
|
|
226
236
|
bug : 'Хата',
|
|
227
237
|
bug_relation : 'Хата бәйләнеше',
|
|
228
238
|
tag : 'Тег',
|
|
239
|
+
custom_property : 'Кулланучы үзлекләре',
|
|
229
240
|
execution_context : 'Башкару контексты',
|
|
230
241
|
task : 'Бурыч',
|
|
231
242
|
team_member : 'Команд әгъзасы',
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { familyTestDocumentation } from "../core/1_familyTestDocumentation.js";
|
|
2
|
+
import { is_object } from "../../utils/utils.js";
|
|
3
|
+
import _ from 'underscore';
|
|
4
|
+
import { typeCustomProperty_i18n } from "../element_translate/typeCustomProperty.i18n.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Team-scoped custom property catalog slot for one element_type (test_case | bug).
|
|
8
|
+
* Server CUD requires member_role = ADMINISTRATOR. Do not set clone_element = false (listing CM).
|
|
9
|
+
*
|
|
10
|
+
* @see docs/CustomProperties/custom-property-docs.MD
|
|
11
|
+
*
|
|
12
|
+
* @version v.0.1 (15/08/2026)
|
|
13
|
+
*/
|
|
14
|
+
export class typeCustomProperty extends familyTestDocumentation
|
|
15
|
+
{
|
|
16
|
+
custom_property_id;
|
|
17
|
+
team_id;
|
|
18
|
+
element_type;
|
|
19
|
+
property_key;
|
|
20
|
+
value_type;
|
|
21
|
+
cardinality;
|
|
22
|
+
name;
|
|
23
|
+
description;
|
|
24
|
+
is_required;
|
|
25
|
+
is_enabled;
|
|
26
|
+
is_searchable;
|
|
27
|
+
sort_order;
|
|
28
|
+
created_at;
|
|
29
|
+
updated_at;
|
|
30
|
+
|
|
31
|
+
/** Hydrated via search options.add: ['options'] */
|
|
32
|
+
options;
|
|
33
|
+
|
|
34
|
+
primary_key = 'custom_property_id';
|
|
35
|
+
api_key = 'custom_property';
|
|
36
|
+
|
|
37
|
+
structure_scheme = {
|
|
38
|
+
custom_property_id: 'integer | require',
|
|
39
|
+
team_id : 'integer | require',
|
|
40
|
+
element_type : 'string | enum | require',
|
|
41
|
+
property_key : 'string | require',
|
|
42
|
+
value_type : 'string | enum | require',
|
|
43
|
+
cardinality : 'string | enum | require',
|
|
44
|
+
name : 'string | require',
|
|
45
|
+
description : 'string | optional',
|
|
46
|
+
is_required : 'integer | optional',
|
|
47
|
+
is_enabled : 'integer | optional',
|
|
48
|
+
is_searchable : 'integer | optional',
|
|
49
|
+
sort_order : 'integer | optional',
|
|
50
|
+
created_at : 'timestamp | optional',
|
|
51
|
+
updated_at : 'timestamp | optional',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
create_scheme = [
|
|
55
|
+
'element_type',
|
|
56
|
+
'property_key',
|
|
57
|
+
'value_type',
|
|
58
|
+
'name',
|
|
59
|
+
'cardinality',
|
|
60
|
+
'description',
|
|
61
|
+
'is_required',
|
|
62
|
+
'is_enabled',
|
|
63
|
+
'is_searchable',
|
|
64
|
+
'sort_order',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
available_enum_values = {
|
|
68
|
+
element_type: typeCustomProperty.get_element_type_values(),
|
|
69
|
+
value_type : typeCustomProperty.get_value_type_values(),
|
|
70
|
+
cardinality : typeCustomProperty.get_cardinality_values(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
enum_value_translate_matrix = typeCustomProperty_i18n.enum_value_translate_matrix;
|
|
74
|
+
|
|
75
|
+
attribute_name_translate_matrix = typeCustomProperty_i18n.attribute_name_translate_matrix;
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
static get_element_type_values()
|
|
79
|
+
{
|
|
80
|
+
return [ 'test_case', 'bug' ];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
static get_value_type_values()
|
|
85
|
+
{
|
|
86
|
+
return [
|
|
87
|
+
'STRING',
|
|
88
|
+
'TEXT',
|
|
89
|
+
'INTEGER',
|
|
90
|
+
'DATE',
|
|
91
|
+
'URL',
|
|
92
|
+
'USER',
|
|
93
|
+
'DROPDOWN',
|
|
94
|
+
'CHECKBOX',
|
|
95
|
+
'MULTISELECT',
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
static get_cardinality_values()
|
|
101
|
+
{
|
|
102
|
+
return [ 'SINGLE', 'MULTIPLE' ];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {object|false|undefined} data
|
|
108
|
+
* @returns {typeCustomProperty}
|
|
109
|
+
*/
|
|
110
|
+
constructor(data = false)
|
|
111
|
+
{
|
|
112
|
+
super();
|
|
113
|
+
|
|
114
|
+
if (data && is_object(data))
|
|
115
|
+
{
|
|
116
|
+
_.mapObject(data, (value, key) =>
|
|
117
|
+
{
|
|
118
|
+
if (this.hasOwnProperty(key))
|
|
119
|
+
{
|
|
120
|
+
this[ key ] = value;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
get_element_name()
|
|
130
|
+
{
|
|
131
|
+
if (this.name)
|
|
132
|
+
{
|
|
133
|
+
return String(this.name);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return String(this.property_key ?? this.custom_property_id ?? '');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -40,6 +40,7 @@ export class familyEvent extends familyGeneralElement
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Каталог subject_type (= api_key), совпадает с seed event-service.
|
|
43
|
+
* `custom_property` — CUD слота каталога; значения на хосте публикуются как test_case/bug UPDATE.
|
|
43
44
|
*
|
|
44
45
|
* @returns {string[]}
|
|
45
46
|
*/
|
|
@@ -61,6 +62,7 @@ export class familyEvent extends familyGeneralElement
|
|
|
61
62
|
'attachment',
|
|
62
63
|
'tag',
|
|
63
64
|
'tag_relation',
|
|
65
|
+
'custom_property',
|
|
64
66
|
'test_account',
|
|
65
67
|
'user_profile',
|
|
66
68
|
'text_document',
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { is_object } from '../../utils/utils.js';
|
|
2
|
+
import _ from 'underscore';
|
|
3
|
+
import { familyRelation } from '../core/1_familyRelation.js';
|
|
4
|
+
import { typeCustomPropertyValue_i18n } from '../element_translate/typeCustomPropertyValue.i18n.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One typed value row on a host element (test_case | bug).
|
|
8
|
+
* Mutations go through CustomPropertyValueApi.set / add / delete, not union create().
|
|
9
|
+
* Server rejects member_role = READONLY (api_code = -501). MEMBER may write.
|
|
10
|
+
*
|
|
11
|
+
* @see docs/CustomProperties/custom-property-docs.MD
|
|
12
|
+
*
|
|
13
|
+
* @version v.0.1 (15/08/2026)
|
|
14
|
+
*/
|
|
15
|
+
export class typeCustomPropertyValue extends familyRelation
|
|
16
|
+
{
|
|
17
|
+
custom_property_value_id;
|
|
18
|
+
custom_property_id;
|
|
19
|
+
team_id;
|
|
20
|
+
project_id;
|
|
21
|
+
element_type;
|
|
22
|
+
element_id;
|
|
23
|
+
sort_order;
|
|
24
|
+
user_id;
|
|
25
|
+
created_at;
|
|
26
|
+
updated_at;
|
|
27
|
+
|
|
28
|
+
string_value;
|
|
29
|
+
text_value;
|
|
30
|
+
integer_value;
|
|
31
|
+
date_value;
|
|
32
|
+
url;
|
|
33
|
+
url_caption;
|
|
34
|
+
user_id_value;
|
|
35
|
+
checkbox_value;
|
|
36
|
+
option_id;
|
|
37
|
+
|
|
38
|
+
source_key = 'custom_property_id';
|
|
39
|
+
target_key = 'element_id';
|
|
40
|
+
relation_type_key = 'element_type';
|
|
41
|
+
|
|
42
|
+
primary_key = 'custom_property_value_id';
|
|
43
|
+
api_key = 'custom_property_value';
|
|
44
|
+
|
|
45
|
+
static relation_target_map = {
|
|
46
|
+
test_case: { type_token: 'typeTestCase', primary_key: 'test_case_id', api_key: 'test_case' },
|
|
47
|
+
bug : { type_token: 'typeBug', primary_key: 'bug_id', api_key: 'bug' },
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
structure_scheme = {
|
|
51
|
+
custom_property_value_id: 'integer | require',
|
|
52
|
+
custom_property_id : 'integer | typeCustomProperty | require',
|
|
53
|
+
team_id : 'integer | require',
|
|
54
|
+
project_id : 'integer | require',
|
|
55
|
+
element_type : 'string | enum | require',
|
|
56
|
+
element_id : 'integer | require',
|
|
57
|
+
sort_order : 'integer | optional',
|
|
58
|
+
user_id : 'integer | optional',
|
|
59
|
+
created_at : 'timestamp | optional',
|
|
60
|
+
updated_at : 'timestamp | optional',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
create_scheme = [
|
|
64
|
+
'custom_property_id',
|
|
65
|
+
'element_type',
|
|
66
|
+
'element_id',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
available_enum_values = {
|
|
70
|
+
element_type: typeCustomPropertyValue.get_relation_type_values(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
enum_value_translate_matrix = typeCustomPropertyValue_i18n.enum_value_translate_matrix;
|
|
74
|
+
|
|
75
|
+
attribute_name_translate_matrix = typeCustomPropertyValue_i18n.attribute_name_translate_matrix;
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
static get_relation_type_values()
|
|
79
|
+
{
|
|
80
|
+
return Object.keys(typeCustomPropertyValue.relation_target_map);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
static get_relation_target_map()
|
|
85
|
+
{
|
|
86
|
+
return typeCustomPropertyValue.relation_target_map;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {object|false|undefined} data
|
|
92
|
+
* @returns {typeCustomPropertyValue}
|
|
93
|
+
*/
|
|
94
|
+
constructor(data = false)
|
|
95
|
+
{
|
|
96
|
+
super();
|
|
97
|
+
|
|
98
|
+
if (data && is_object(data))
|
|
99
|
+
{
|
|
100
|
+
_.mapObject(data, (value, key) =>
|
|
101
|
+
{
|
|
102
|
+
if (this.hasOwnProperty(key))
|
|
103
|
+
{
|
|
104
|
+
this[ key ] = value;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
get_relation_target_meta()
|
|
114
|
+
{
|
|
115
|
+
return typeCustomPropertyValue.resolve_relation_target(this.element_type);
|
|
116
|
+
}
|
|
117
|
+
}
|