claude-mpm 4.15.6__py3-none-any.whl → 4.21.3__py3-none-any.whl
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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +272 -23
- claude_mpm/agents/PM_INSTRUCTIONS.md +49 -0
- claude_mpm/agents/agent_loader.py +4 -4
- claude_mpm/agents/templates/engineer.json +5 -1
- claude_mpm/agents/templates/php-engineer.json +10 -4
- claude_mpm/agents/templates/python_engineer.json +8 -3
- claude_mpm/agents/templates/rust_engineer.json +12 -7
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +525 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +67 -1
- claude_mpm/cli/commands/skills.py +488 -0
- claude_mpm/cli/executor.py +2 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +42 -0
- claude_mpm/cli/parsers/skills_parser.py +137 -0
- claude_mpm/cli/startup.py +57 -0
- claude_mpm/commands/mpm-auto-configure.md +52 -0
- claude_mpm/commands/mpm-help.md +6 -0
- claude_mpm/commands/mpm-init.md +112 -6
- claude_mpm/commands/mpm-resume.md +372 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +2 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/constants.py +12 -0
- claude_mpm/core/config.py +42 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/interfaces.py +56 -1
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/hooks/__init__.py +8 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/recommender.py +47 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +87 -0
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/core/base.py +26 -11
- claude_mpm/services/core/interfaces.py +56 -1
- claude_mpm/services/core/models/agent_config.py +3 -0
- claude_mpm/services/core/models/process.py +4 -0
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/models.py +21 -0
- claude_mpm/services/event_bus/relay.py +23 -7
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +2 -0
- claude_mpm/services/mcp_config_manager.py +7 -131
- claude_mpm/services/mcp_gateway/auto_configure.py +31 -25
- claude_mpm/services/mcp_gateway/core/process_pool.py +19 -10
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +26 -21
- claude_mpm/services/memory/failure_tracker.py +19 -4
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/unified/deployment_strategies/local.py +1 -1
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +97 -9
- claude_mpm/skills/skills_registry.py +348 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/METADATA +211 -33
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/RECORD +206 -64
- claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md +0 -602
- claude_mpm/cli/commands/mpm_init.py +0 -2008
- claude_mpm/tools/code_tree_analyzer.py +0 -1825
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
# Frontend Customization Reference
|
|
2
|
+
|
|
3
|
+
## EspoCRM View System
|
|
4
|
+
|
|
5
|
+
EspoCRM uses a custom View architecture (not Backbone.View or Knockout). Views follow a specific lifecycle and pattern.
|
|
6
|
+
|
|
7
|
+
### View Lifecycle
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
constructor → setup() → afterRender() → user interaction → onRemove()
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Basic View Structure
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
define('custom:views/my-custom-view', ['view'], function (Dep) {
|
|
17
|
+
|
|
18
|
+
return Dep.extend({
|
|
19
|
+
|
|
20
|
+
// Template path (optional)
|
|
21
|
+
template: 'custom:my-custom-view',
|
|
22
|
+
|
|
23
|
+
// Events (optional)
|
|
24
|
+
events: {
|
|
25
|
+
'click [data-action="save"]': function (e) {
|
|
26
|
+
this.actionSave();
|
|
27
|
+
},
|
|
28
|
+
'change input[name="status"]': function (e) {
|
|
29
|
+
this.handleStatusChange(e);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Data for template
|
|
34
|
+
data: function () {
|
|
35
|
+
return {
|
|
36
|
+
name: this.model.get('name'),
|
|
37
|
+
status: this.model.get('status'),
|
|
38
|
+
customValue: this.options.customValue
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// Setup (called before rendering)
|
|
43
|
+
setup: function () {
|
|
44
|
+
Dep.prototype.setup.call(this);
|
|
45
|
+
|
|
46
|
+
// Initialize properties
|
|
47
|
+
this.customProperty = this.options.customProperty || 'default';
|
|
48
|
+
|
|
49
|
+
// Listen to model changes
|
|
50
|
+
this.listenTo(this.model, 'change:status', function () {
|
|
51
|
+
this.reRender();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Create child views
|
|
55
|
+
this.createView('myChild', 'custom:views/my-child-view', {
|
|
56
|
+
el: this.getSelector() + ' .child-container',
|
|
57
|
+
model: this.model
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// After render (DOM is ready)
|
|
62
|
+
afterRender: function () {
|
|
63
|
+
Dep.prototype.afterRender.call(this);
|
|
64
|
+
|
|
65
|
+
// DOM manipulation
|
|
66
|
+
this.$el.find('.my-element').addClass('active');
|
|
67
|
+
|
|
68
|
+
// Initialize plugins
|
|
69
|
+
this.$el.find('[data-toggle="tooltip"]').tooltip();
|
|
70
|
+
|
|
71
|
+
// Fetch data
|
|
72
|
+
this.loadAdditionalData();
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Cleanup (called when view is removed)
|
|
76
|
+
onRemove: function () {
|
|
77
|
+
Dep.prototype.onRemove.call(this);
|
|
78
|
+
|
|
79
|
+
// Cleanup listeners, timers, etc.
|
|
80
|
+
if (this.intervalId) {
|
|
81
|
+
clearInterval(this.intervalId);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// Custom methods
|
|
86
|
+
actionSave: function () {
|
|
87
|
+
var data = {
|
|
88
|
+
name: this.$el.find('[name="name"]').val(),
|
|
89
|
+
status: this.$el.find('[name="status"]').val()
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
this.model.save(data, {
|
|
93
|
+
patch: true
|
|
94
|
+
}).then(function () {
|
|
95
|
+
Espo.Ui.success(this.translate('Saved'));
|
|
96
|
+
}.bind(this));
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
handleStatusChange: function (e) {
|
|
100
|
+
var status = $(e.currentTarget).val();
|
|
101
|
+
console.log('Status changed to:', status);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
loadAdditionalData: function () {
|
|
105
|
+
this.ajaxGetRequest('MyEntity/' + this.model.id + '/additionalData')
|
|
106
|
+
.then(function (response) {
|
|
107
|
+
this.additionalData = response;
|
|
108
|
+
this.reRender();
|
|
109
|
+
}.bind(this));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Custom Record Views
|
|
116
|
+
|
|
117
|
+
### Detail View Customization
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
define('custom:views/account/record/detail', ['views/record/detail'], function (Dep) {
|
|
121
|
+
|
|
122
|
+
return Dep.extend({
|
|
123
|
+
|
|
124
|
+
setup: function () {
|
|
125
|
+
Dep.prototype.setup.call(this);
|
|
126
|
+
|
|
127
|
+
// Add custom button
|
|
128
|
+
this.addButton({
|
|
129
|
+
name: 'customAction',
|
|
130
|
+
label: 'Custom Action',
|
|
131
|
+
style: 'primary'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Add dropdown action
|
|
135
|
+
this.addDropdownItem({
|
|
136
|
+
name: 'exportToPdf',
|
|
137
|
+
label: 'Export to PDF',
|
|
138
|
+
action: 'exportToPdf'
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Hide default button
|
|
142
|
+
this.removeButton('delete');
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
actionCustomAction: function () {
|
|
146
|
+
// Button clicked
|
|
147
|
+
this.confirm(this.translate('Are you sure?'), function () {
|
|
148
|
+
this.performCustomAction();
|
|
149
|
+
}, this);
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
performCustomAction: function () {
|
|
153
|
+
Espo.Ui.notify(this.translate('Loading...'));
|
|
154
|
+
|
|
155
|
+
this.ajaxPostRequest('Account/' + this.model.id + '/customAction', {
|
|
156
|
+
param: 'value'
|
|
157
|
+
}).then(function (response) {
|
|
158
|
+
Espo.Ui.success(this.translate('Done'));
|
|
159
|
+
this.model.fetch();
|
|
160
|
+
}.bind(this));
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
actionExportToPdf: function () {
|
|
164
|
+
window.open('?entryPoint=download&id=' + this.model.id, '_blank');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Edit View Customization
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
define('custom:views/opportunity/record/edit', ['views/record/edit'], function (Dep) {
|
|
174
|
+
|
|
175
|
+
return Dep.extend({
|
|
176
|
+
|
|
177
|
+
setup: function () {
|
|
178
|
+
Dep.prototype.setup.call(this);
|
|
179
|
+
|
|
180
|
+
// Dynamic field logic
|
|
181
|
+
this.controlFieldVisibility();
|
|
182
|
+
|
|
183
|
+
// Listen for field changes
|
|
184
|
+
this.listenTo(this.model, 'change:stage', function () {
|
|
185
|
+
this.controlFieldVisibility();
|
|
186
|
+
}, this);
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
controlFieldVisibility: function () {
|
|
190
|
+
var stage = this.model.get('stage');
|
|
191
|
+
|
|
192
|
+
if (stage === 'Closed Won') {
|
|
193
|
+
this.showField('closeDate');
|
|
194
|
+
this.setFieldRequired('closeDate');
|
|
195
|
+
} else {
|
|
196
|
+
this.hideField('closeDate');
|
|
197
|
+
this.setFieldNotRequired('closeDate');
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Override save to add custom logic
|
|
202
|
+
save: function () {
|
|
203
|
+
// Custom validation
|
|
204
|
+
var amount = this.model.get('amount');
|
|
205
|
+
if (amount > 1000000) {
|
|
206
|
+
this.notify('Please get manager approval for deals over $1M', 'warning');
|
|
207
|
+
return Promise.reject();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Call parent save
|
|
211
|
+
return Dep.prototype.save.call(this);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### List View Customization
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
define('custom:views/account/record/list', ['views/record/list'], function (Dep) {
|
|
221
|
+
|
|
222
|
+
return Dep.extend({
|
|
223
|
+
|
|
224
|
+
// Add mass action
|
|
225
|
+
massActionList: Dep.prototype.massActionList.concat([
|
|
226
|
+
'exportToExcel',
|
|
227
|
+
'sendEmail'
|
|
228
|
+
]),
|
|
229
|
+
|
|
230
|
+
setup: function () {
|
|
231
|
+
Dep.prototype.setup.call(this);
|
|
232
|
+
|
|
233
|
+
// Add custom row action
|
|
234
|
+
this.addRowAction('viewWebsite', {
|
|
235
|
+
label: 'View Website',
|
|
236
|
+
action: 'viewWebsite'
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
// Handle mass action
|
|
241
|
+
massActionExportToExcel: function () {
|
|
242
|
+
var ids = this.getSelected();
|
|
243
|
+
|
|
244
|
+
this.ajaxPostRequest('Account/action/exportToExcel', {
|
|
245
|
+
ids: ids
|
|
246
|
+
}).then(function (response) {
|
|
247
|
+
window.location = response.downloadUrl;
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
// Handle row action
|
|
252
|
+
actionViewWebsite: function (data) {
|
|
253
|
+
var model = this.collection.get(data.id);
|
|
254
|
+
var website = model.get('website');
|
|
255
|
+
|
|
256
|
+
if (website) {
|
|
257
|
+
window.open(website, '_blank');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Custom Field Views
|
|
265
|
+
|
|
266
|
+
### Creating Custom Field View
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
define('custom:views/fields/color-picker', ['views/fields/varchar'], function (Dep) {
|
|
270
|
+
|
|
271
|
+
return Dep.extend({
|
|
272
|
+
|
|
273
|
+
// Detail mode template
|
|
274
|
+
detailTemplate: 'custom:fields/color-picker/detail',
|
|
275
|
+
|
|
276
|
+
// Edit mode template
|
|
277
|
+
editTemplate: 'custom:fields/color-picker/edit',
|
|
278
|
+
|
|
279
|
+
// Events specific to this field
|
|
280
|
+
events: {
|
|
281
|
+
'change input.color-input': function (e) {
|
|
282
|
+
this.trigger('change');
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
// Setup
|
|
287
|
+
setup: function () {
|
|
288
|
+
Dep.prototype.setup.call(this);
|
|
289
|
+
|
|
290
|
+
this.defaultColor = this.params.defaultColor || '#000000';
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// After render in edit mode
|
|
294
|
+
afterRenderEdit: function () {
|
|
295
|
+
Dep.prototype.afterRenderEdit.call(this);
|
|
296
|
+
|
|
297
|
+
// Initialize color picker plugin
|
|
298
|
+
this.$el.find('input.color-input').spectrum({
|
|
299
|
+
preferredFormat: 'hex',
|
|
300
|
+
showInput: true,
|
|
301
|
+
allowEmpty: true
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// Fetch value from DOM
|
|
306
|
+
fetch: function () {
|
|
307
|
+
var value = this.$el.find('input.color-input').val();
|
|
308
|
+
|
|
309
|
+
var data = {};
|
|
310
|
+
data[this.name] = value || null;
|
|
311
|
+
|
|
312
|
+
return data;
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
// Validation
|
|
316
|
+
validateRequired: function () {
|
|
317
|
+
if (this.isRequired()) {
|
|
318
|
+
var value = this.model.get(this.name);
|
|
319
|
+
|
|
320
|
+
if (!value) {
|
|
321
|
+
var msg = this.translate('fieldIsRequired', 'messages')
|
|
322
|
+
.replace('{field}', this.getLabelText());
|
|
323
|
+
|
|
324
|
+
this.showValidationMessage(msg);
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
// Custom validation
|
|
331
|
+
validate: function () {
|
|
332
|
+
if (Dep.prototype.validate.call(this)) {
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
var value = this.model.get(this.name);
|
|
337
|
+
|
|
338
|
+
if (value && !this.isValidHexColor(value)) {
|
|
339
|
+
var msg = 'Invalid color format';
|
|
340
|
+
this.showValidationMessage(msg);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return false;
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
isValidHexColor: function (color) {
|
|
348
|
+
return /^#[0-9A-F]{6}$/i.test(color);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Field Template (Handlebars)
|
|
355
|
+
|
|
356
|
+
Detail template (`client/custom/res/templates/fields/color-picker/detail.tpl`):
|
|
357
|
+
```handlebars
|
|
358
|
+
<div class="color-preview" style="background-color: {{value}}; width: 50px; height: 20px; border: 1px solid #ccc;"></div>
|
|
359
|
+
<span>{{value}}</span>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Edit template (`client/custom/res/templates/fields/color-picker/edit.tpl`):
|
|
363
|
+
```handlebars
|
|
364
|
+
<input
|
|
365
|
+
type="text"
|
|
366
|
+
class="form-control color-input"
|
|
367
|
+
name="{{name}}"
|
|
368
|
+
value="{{value}}"
|
|
369
|
+
autocomplete="espo-{{name}}"
|
|
370
|
+
>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## AJAX Helpers
|
|
374
|
+
|
|
375
|
+
### Making AJAX Requests
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
// GET request
|
|
379
|
+
this.ajaxGetRequest('Account/' + id)
|
|
380
|
+
.then(function (response) {
|
|
381
|
+
console.log(response);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// POST request
|
|
385
|
+
this.ajaxPostRequest('Account/action/customAction', {
|
|
386
|
+
param1: 'value1',
|
|
387
|
+
param2: 'value2'
|
|
388
|
+
}).then(function (response) {
|
|
389
|
+
console.log(response);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// PUT request
|
|
393
|
+
this.ajaxPutRequest('Account/' + id, {
|
|
394
|
+
name: 'Updated Name'
|
|
395
|
+
}).then(function (response) {
|
|
396
|
+
console.log(response);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// DELETE request
|
|
400
|
+
this.ajaxDeleteRequest('Account/' + id)
|
|
401
|
+
.then(function () {
|
|
402
|
+
console.log('Deleted');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// With error handling
|
|
406
|
+
this.ajaxPostRequest('Account/action/risky', data)
|
|
407
|
+
.then(function (response) {
|
|
408
|
+
Espo.Ui.success('Success');
|
|
409
|
+
})
|
|
410
|
+
.catch(function (xhr) {
|
|
411
|
+
var reason = xhr.responseJSON?.reason || 'Unknown error';
|
|
412
|
+
Espo.Ui.error(reason);
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Notifications and Dialogs
|
|
417
|
+
|
|
418
|
+
### UI Notifications
|
|
419
|
+
|
|
420
|
+
```javascript
|
|
421
|
+
// Success notification
|
|
422
|
+
Espo.Ui.success(this.translate('Saved'));
|
|
423
|
+
|
|
424
|
+
// Error notification
|
|
425
|
+
Espo.Ui.error(this.translate('Error occurred'));
|
|
426
|
+
|
|
427
|
+
// Warning notification
|
|
428
|
+
Espo.Ui.warning(this.translate('Please review'));
|
|
429
|
+
|
|
430
|
+
// Info notification
|
|
431
|
+
Espo.Ui.notify(this.translate('Loading...'));
|
|
432
|
+
|
|
433
|
+
// Remove notification
|
|
434
|
+
Espo.Ui.notify(false);
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Confirmation Dialogs
|
|
438
|
+
|
|
439
|
+
```javascript
|
|
440
|
+
// Simple confirmation
|
|
441
|
+
this.confirm('Are you sure?', function () {
|
|
442
|
+
// User clicked OK
|
|
443
|
+
this.performAction();
|
|
444
|
+
}, this);
|
|
445
|
+
|
|
446
|
+
// Confirmation with translated message
|
|
447
|
+
this.confirm(
|
|
448
|
+
this.translate('confirmDeletion', 'messages'),
|
|
449
|
+
function () {
|
|
450
|
+
this.delete();
|
|
451
|
+
},
|
|
452
|
+
this
|
|
453
|
+
);
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Custom Dialogs
|
|
457
|
+
|
|
458
|
+
```javascript
|
|
459
|
+
// Create dialog view
|
|
460
|
+
this.createView('dialog', 'custom:views/modals/my-dialog', {
|
|
461
|
+
model: this.model
|
|
462
|
+
}, function (view) {
|
|
463
|
+
view.render();
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Dialog with callback
|
|
467
|
+
this.createView('dialog', 'views/modals/edit', {
|
|
468
|
+
scope: 'Account',
|
|
469
|
+
id: accountId
|
|
470
|
+
}, function (view) {
|
|
471
|
+
view.render();
|
|
472
|
+
|
|
473
|
+
this.listenToOnce(view, 'after:save', function () {
|
|
474
|
+
// Dialog saved
|
|
475
|
+
this.model.fetch();
|
|
476
|
+
}, this);
|
|
477
|
+
}, this);
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Custom Controllers
|
|
481
|
+
|
|
482
|
+
```javascript
|
|
483
|
+
define('custom:controllers/my-entity', ['controllers/record'], function (Dep) {
|
|
484
|
+
|
|
485
|
+
return Dep.extend({
|
|
486
|
+
|
|
487
|
+
// Default action
|
|
488
|
+
defaultAction: 'list',
|
|
489
|
+
|
|
490
|
+
// Custom action
|
|
491
|
+
actionCustomDashboard: function (options) {
|
|
492
|
+
this.main('custom:views/my-entity/dashboard', {
|
|
493
|
+
scope: this.name
|
|
494
|
+
});
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
// Before default action
|
|
498
|
+
beforeList: function () {
|
|
499
|
+
Dep.prototype.beforeList.call(this);
|
|
500
|
+
console.log('Before list action');
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// After default action
|
|
504
|
+
afterList: function () {
|
|
505
|
+
Dep.prototype.afterList.call(this);
|
|
506
|
+
console.log('After list action');
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## Working with Models
|
|
513
|
+
|
|
514
|
+
### Model Operations
|
|
515
|
+
|
|
516
|
+
```javascript
|
|
517
|
+
// Get model value
|
|
518
|
+
var name = this.model.get('name');
|
|
519
|
+
|
|
520
|
+
// Set model value (doesn't save)
|
|
521
|
+
this.model.set('name', 'New Name');
|
|
522
|
+
|
|
523
|
+
// Set multiple values
|
|
524
|
+
this.model.set({
|
|
525
|
+
name: 'New Name',
|
|
526
|
+
status: 'Active'
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Save model
|
|
530
|
+
this.model.save().then(function () {
|
|
531
|
+
Espo.Ui.success('Saved');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// Save specific attributes (PATCH)
|
|
535
|
+
this.model.save({
|
|
536
|
+
status: 'Complete'
|
|
537
|
+
}, {
|
|
538
|
+
patch: true
|
|
539
|
+
}).then(function () {
|
|
540
|
+
console.log('Status updated');
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Fetch model from server
|
|
544
|
+
this.model.fetch().then(function () {
|
|
545
|
+
console.log('Model refreshed');
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Delete model
|
|
549
|
+
this.model.destroy().then(function () {
|
|
550
|
+
console.log('Deleted');
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// Check if model is new
|
|
554
|
+
if (this.model.isNew()) {
|
|
555
|
+
console.log('New record');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Check if attribute changed
|
|
559
|
+
if (this.model.hasChanged('status')) {
|
|
560
|
+
console.log('Status changed');
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Get previous value
|
|
564
|
+
var previousStatus = this.model.previous('status');
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Model Events
|
|
568
|
+
|
|
569
|
+
```javascript
|
|
570
|
+
// Listen to any change
|
|
571
|
+
this.listenTo(this.model, 'change', function () {
|
|
572
|
+
console.log('Model changed');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Listen to specific attribute change
|
|
576
|
+
this.listenTo(this.model, 'change:status', function (model, value) {
|
|
577
|
+
console.log('Status changed to:', value);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// Listen to save
|
|
581
|
+
this.listenTo(this.model, 'sync', function () {
|
|
582
|
+
console.log('Model saved');
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
// Listen once
|
|
586
|
+
this.listenToOnce(this.model, 'change:status', function () {
|
|
587
|
+
console.log('First status change');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Stop listening
|
|
591
|
+
this.stopListening(this.model, 'change:status');
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
## Collections
|
|
595
|
+
|
|
596
|
+
### Working with Collections
|
|
597
|
+
|
|
598
|
+
```javascript
|
|
599
|
+
// Fetch collection
|
|
600
|
+
this.collection.fetch().then(function () {
|
|
601
|
+
console.log('Collection loaded:', this.collection.length);
|
|
602
|
+
}.bind(this));
|
|
603
|
+
|
|
604
|
+
// Iterate collection
|
|
605
|
+
this.collection.forEach(function (model) {
|
|
606
|
+
console.log(model.get('name'));
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Filter collection
|
|
610
|
+
var activeModels = this.collection.filter(function (model) {
|
|
611
|
+
return model.get('status') === 'Active';
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// Find in collection
|
|
615
|
+
var model = this.collection.find(function (model) {
|
|
616
|
+
return model.id === targetId;
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Get by ID
|
|
620
|
+
var model = this.collection.get(id);
|
|
621
|
+
|
|
622
|
+
// Collection events
|
|
623
|
+
this.listenTo(this.collection, 'sync', function () {
|
|
624
|
+
console.log('Collection synced');
|
|
625
|
+
});
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
## Metadata Access
|
|
629
|
+
|
|
630
|
+
```javascript
|
|
631
|
+
// Get entity metadata
|
|
632
|
+
var entityDefs = this.getMetadata().get(['entityDefs', 'Account']);
|
|
633
|
+
|
|
634
|
+
// Get field definitions
|
|
635
|
+
var fields = this.getMetadata().get(['entityDefs', 'Account', 'fields']);
|
|
636
|
+
|
|
637
|
+
// Get specific field metadata
|
|
638
|
+
var nameFieldDef = this.getMetadata().get(['entityDefs', 'Account', 'fields', 'name']);
|
|
639
|
+
|
|
640
|
+
// Get client definitions
|
|
641
|
+
var clientDefs = this.getMetadata().get(['clientDefs', 'Account']);
|
|
642
|
+
|
|
643
|
+
// Check if entity has field
|
|
644
|
+
var hasField = this.getMetadata().get(['entityDefs', 'Account', 'fields', 'website']);
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
## Translation
|
|
648
|
+
|
|
649
|
+
```javascript
|
|
650
|
+
// Translate label
|
|
651
|
+
var label = this.translate('Account', 'scopeNames');
|
|
652
|
+
|
|
653
|
+
// Translate with category
|
|
654
|
+
var requiredMsg = this.translate('fieldIsRequired', 'messages');
|
|
655
|
+
|
|
656
|
+
// Translate field label
|
|
657
|
+
var nameLabel = this.translate('name', 'fields', 'Account');
|
|
658
|
+
|
|
659
|
+
// Translate option
|
|
660
|
+
var statusLabel = this.translate('Active', 'status', 'Account');
|
|
661
|
+
|
|
662
|
+
// String replacement
|
|
663
|
+
var msg = this.translate('recordSaved', 'messages')
|
|
664
|
+
.replace('{record}', this.model.get('name'));
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
## Best Practices
|
|
668
|
+
|
|
669
|
+
### 1. Always Call Parent Methods
|
|
670
|
+
|
|
671
|
+
```javascript
|
|
672
|
+
// ✅ CORRECT
|
|
673
|
+
setup: function () {
|
|
674
|
+
Dep.prototype.setup.call(this);
|
|
675
|
+
// Your code
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// ❌ WRONG - Missing parent call
|
|
679
|
+
setup: function () {
|
|
680
|
+
// Your code only
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### 2. Proper Cleanup in onRemove
|
|
685
|
+
|
|
686
|
+
```javascript
|
|
687
|
+
// ✅ CORRECT
|
|
688
|
+
onRemove: function () {
|
|
689
|
+
Dep.prototype.onRemove.call(this);
|
|
690
|
+
|
|
691
|
+
// Cleanup timers
|
|
692
|
+
if (this.interval) {
|
|
693
|
+
clearInterval(this.interval);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Cleanup event listeners (automatically done by listenTo)
|
|
697
|
+
// Manual jQuery listeners need cleanup
|
|
698
|
+
$(window).off('resize.myView');
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
### 3. Use this.listenTo Instead of model.on
|
|
703
|
+
|
|
704
|
+
```javascript
|
|
705
|
+
// ✅ CORRECT - Auto cleanup
|
|
706
|
+
this.listenTo(this.model, 'change', callback);
|
|
707
|
+
|
|
708
|
+
// ❌ WRONG - Memory leak, manual cleanup needed
|
|
709
|
+
this.model.on('change', callback);
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### 4. Proper Promise Handling
|
|
713
|
+
|
|
714
|
+
```javascript
|
|
715
|
+
// ✅ CORRECT
|
|
716
|
+
this.model.save().then(function () {
|
|
717
|
+
Espo.Ui.success('Saved');
|
|
718
|
+
}.bind(this)).catch(function () {
|
|
719
|
+
Espo.Ui.error('Error');
|
|
720
|
+
}.bind(this));
|
|
721
|
+
|
|
722
|
+
// ❌ WRONG - Unhandled rejection
|
|
723
|
+
this.model.save().then(function () {
|
|
724
|
+
Espo.Ui.success('Saved');
|
|
725
|
+
});
|
|
726
|
+
```
|