Compare commits

...

4 Commits

Author SHA1 Message Date
chaim 81fa5f3a08 feat: auto-refresh panels after Shira executes an action
When Shira creates a task, meeting, or other entity, the current
detail view panels now refresh automatically via model.trigger('update-all').
For tasks and meetings, also refreshes the activities/tasks side panels.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 15:53:01 +00:00
chaim cf0e9ad2bc fix: stdClass as array error in ConversationRepository
EspoCRM returns JSON data as stdClass objects, not arrays. Using
array access ($data[$i]['role']) on stdClass causes fatal error.
Fixed by using json_decode(json_encode(...), true) to ensure deep
conversion to associative arrays.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:33:25 +00:00
chaim 07df19b412 fix: stream notes showing empty content - add data() method
SmartRequest and SmartAction note views were missing data() method,
so {{post}} in the template resolved to empty string. Added proper
data() with escapeString formatting, consistent with SmartResponse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:09:05 +00:00
chaim b9542e3980 fix: add missing var self = this in showDetail()
self was resolving to window.self instead of the View instance
inside the forEach callback, causing self.escapeString to fail.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:05:30 +00:00
7 changed files with 59 additions and 12 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# SmartAssistant - עוזר חכם # SmartAssistant - עוזר חכם
**גרסה:** 1.1.3 | **מחבר:** klear | **EspoCRM:** >= 8.0.0 **גרסה:** 1.1.7 | **מחבר:** klear | **EspoCRM:** >= 8.0.0
## תיאור ## תיאור
עוזר AI מאוחד למשרד עורכי דין. מספק ממשק צ'אט צף עם שני מצבי עבודה: **מצב משרד** (סקירה כללית, התראות, סטטיסטיקות) ו**מצב תיק** (סיוע מעמיק בתיק ספציפי). כולל מערכת זיכרון תיק מובנית, ביצוע פעולות באישור המשתמש, ואינטגרציה עם Stream של EspoCRM. עוזר AI מאוחד למשרד עורכי דין. מספק ממשק צ'אט צף עם שני מצבי עבודה: **מצב משרד** (סקירה כללית, התראות, סטטיסטיקות) ו**מצב תיק** (סיוע מעמיק בתיק ספציפי). כולל מערכת זיכרון תיק מובנית, ביצוע פעולות באישור המשתמש, ואינטגרציה עם Stream של EspoCRM.
@@ -76,6 +76,7 @@ define('modules/smart-assistant/views/dashlets/smart-assistant', ['view'], funct
}, },
showDetail: function (type) { showDetail: function (type) {
var self = this;
var $detail = this.$el.find('.sa-dashlet-detail'); var $detail = this.$el.find('.sa-dashlet-detail');
if (!type || type === this._activeType && $detail.html()) { if (!type || type === this._activeType && $detail.html()) {
@@ -480,6 +480,10 @@ define('modules/smart-assistant/views/floating-chat', ['view'], function (View)
var cls = approved ? (result.success ? 'sa-msg-assistant' : 'sa-msg-error') : 'sa-msg-loading'; var cls = approved ? (result.success ? 'sa-msg-assistant' : 'sa-msg-error') : 'sa-msg-loading';
$messages.append('<div class="sa-msg ' + cls + '">' + self.escapeHtml(msg) + '</div>'); $messages.append('<div class="sa-msg ' + cls + '">' + self.escapeHtml(msg) + '</div>');
self.scrollToBottom(); self.scrollToBottom();
if (approved && result.success) {
self.refreshCurrentView(result.entityType);
}
}).catch(function () { }).catch(function () {
$messages.append('<div class="sa-msg sa-msg-error">שגיאה בביצוע הפעולה</div>'); $messages.append('<div class="sa-msg sa-msg-error">שגיאה בביצוע הפעולה</div>');
self.scrollToBottom(); self.scrollToBottom();
@@ -620,5 +624,37 @@ define('modules/smart-assistant/views/floating-chat', ['view'], function (View)
div.textContent = text; div.textContent = text;
return div.innerHTML; return div.innerHTML;
}, },
refreshCurrentView: function (entityType) {
var mainView = this.getParentView();
while (mainView && mainView.getParentView()) {
mainView = mainView.getParentView();
}
if (!mainView) return;
var currentView = mainView.getView('main');
if (!currentView) return;
var recordView = currentView.getView('record');
if (recordView && recordView.model) {
recordView.model.trigger('update-all');
}
if (entityType === 'Task' || entityType === 'Meeting') {
var sideView = currentView.getView('side');
if (sideView) {
var activities = sideView.getView('activities');
var tasks = sideView.getView('tasks');
if (activities && activities.actionRefresh) activities.actionRefresh();
if (tasks && tasks.actionRefresh) tasks.actionRefresh();
}
}
},
}); });
}); });
@@ -5,7 +5,7 @@ define('modules/smart-assistant/views/stream/notes/smart-action', ['views/stream
templateContent: '' + templateContent: '' +
'<div class="sa-action-note sa-action-{{status}}" style="padding: 6px 12px; border-radius: 8px; direction: rtl; text-align: right; font-size: 13px;">' + '<div class="sa-action-note sa-action-{{status}}" style="padding: 6px 12px; border-radius: 8px; direction: rtl; text-align: right; font-size: 13px;">' +
'<span class="fas {{icon}}" style="margin-left: 6px;"></span>' + '<span class="fas {{icon}}" style="margin-left: 6px;"></span>' +
'{{post}}' + '{{{formattedPost}}}' +
'</div>', '</div>',
data: function () { data: function () {
@@ -13,6 +13,8 @@ define('modules/smart-assistant/views/stream/notes/smart-action', ['views/stream
var noteData = this.model.get('data') || {}; var noteData = this.model.get('data') || {};
var status = noteData.status || 'executed'; var status = noteData.status || 'executed';
data.post = this.model.get('post');
data.formattedPost = this.getHelper().escapeString(data.post || '');
data.status = status; data.status = status;
if (status === 'executed') { if (status === 'executed') {
@@ -5,11 +5,22 @@ define('modules/smart-assistant/views/stream/notes/smart-request', ['views/strea
templateContent: '' + templateContent: '' +
'<div style="background: #e3f2fd; padding: 8px 12px; border-radius: 8px; direction: rtl; text-align: right; font-size: 13px;">' + '<div style="background: #e3f2fd; padding: 8px 12px; border-radius: 8px; direction: rtl; text-align: right; font-size: 13px;">' +
'<span class="fas fa-user" style="color: #1565c0; margin-left: 6px;"></span>' + '<span class="fas fa-user" style="color: #1565c0; margin-left: 6px;"></span>' +
'{{post}}' + '{{{formattedPost}}}' +
'</div>', '</div>',
setup: function () { data: function () {
NoteView.prototype.setup.call(this); var data = NoteView.prototype.data.call(this);
data.formattedPost = this.formatPost(this.model.get('post'));
return data;
},
formatPost: function (text) {
if (!text) return '';
text = this.getHelper().escapeString(text);
text = text.replace(/\n/g, '<br>');
return text;
}, },
}); });
}); });
@@ -55,8 +55,7 @@ class ConversationRepository
if (!$entity) return; if (!$entity) return;
} }
$messages = $entity->get('messagesData') ?? []; $messages = json_decode(json_encode($entity->get('messagesData') ?? []), true);
if (is_object($messages)) $messages = (array) $messages;
$newMessage = ['role' => $role, 'content' => $content, 'timestamp' => date('Y-m-d H:i:s')]; $newMessage = ['role' => $role, 'content' => $content, 'timestamp' => date('Y-m-d H:i:s')];
if (!empty($actions)) $newMessage['actions'] = $actions; if (!empty($actions)) $newMessage['actions'] = $actions;
@@ -81,8 +80,7 @@ class ConversationRepository
$entity = $this->entityManager->getRDBRepository('AssistantConversation') $entity = $this->entityManager->getRDBRepository('AssistantConversation')
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne(); ->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if (!$entity) return []; if (!$entity) return [];
$messages = $entity->get('messagesData') ?? []; return json_decode(json_encode($entity->get('messagesData') ?? []), true);
return is_object($messages) ? (array) $messages : $messages;
} }
public function getRecentConversations(?string $type = null, ?string $caseId = null, int $limit = 20): array public function getRecentConversations(?string $type = null, ?string $caseId = null, int $limit = 20): array
@@ -137,8 +135,7 @@ class ConversationRepository
->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne(); ->where(['conversationKey' => $conversationKey, 'deleted' => false])->findOne();
if (!$entity) return; if (!$entity) return;
$data = $entity->get('messagesData') ?? []; $data = json_decode(json_encode($entity->get('messagesData') ?? []), true);
if (is_object($data)) $data = (array) $data;
for ($i = count($data) - 1; $i >= 0; $i--) { for ($i = count($data) - 1; $i >= 0; $i--) {
if (($data[$i]['role'] ?? '') === 'assistant') { $data[$i]['pendingActions'] = $actions; break; } if (($data[$i]['role'] ?? '') === 'assistant') { $data[$i]['pendingActions'] = $actions; break; }
} }
+1 -1
View File
@@ -3,7 +3,7 @@
"module": "SmartAssistant", "module": "SmartAssistant",
"description": "Unified AI Assistant for Legal CRM — floating chat with case memory, office alerts, and n8n integration", "description": "Unified AI Assistant for Legal CRM — floating chat with case memory, office alerts, and n8n integration",
"author": "klear", "author": "klear",
"version": "1.1.3", "version": "1.1.7",
"acceptableVersions": [ "acceptableVersions": [
">=8.0.0" ">=8.0.0"
], ],