@progress/kendo-angular-grid
Version:
Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.
306 lines (305 loc) • 15.6 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { Component, ViewChild } from '@angular/core';
import { AIPromptComponent, OutputViewComponent, PromptViewComponent, AIPromptCustomMessagesComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective } from '@progress/kendo-angular-conversational-ui';
import { HttpClient, HttpRequest } from '@angular/common/http';
import { ContextService } from './../../../../common/provider.service';
import { ColumnInfoService } from './../../../../common/column-info.service';
import { convertDateStringsInFilter, GridToolbarAIResponseSuccessEvent, GridToolbarAIResponseErrorEvent } from './utils';
import { NgIf } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
import * as i2 from "./../../../../common/provider.service";
import * as i3 from "./../../../../common/column-info.service";
/**
* @hidden
*/
export class AiAssistantComponent {
http;
ctx;
columnInfoService;
aiPrompt;
activeView = 0;
requestUrl;
requestOptions;
aiPromptSettings;
aiToolDirective;
streaming = false;
disabledGenerateButton = false;
lastMessage;
requestData;
currentRequestSubscription = null;
//Remove this when the AI Assistant has a built-in loading indicator
loadingOutput = { id: 'k-loading-item', output: '', prompt: '' };
columns = [];
idCounter = 0;
constructor(http, ctx, columnInfoService) {
this.http = http;
this.ctx = ctx;
this.columnInfoService = columnInfoService;
}
ngAfterViewInit() {
this.columns = this.columnInfoService.leafNamedColumns.map((col) => ({
field: col.field,
title: col.title || col.field
}));
}
ngOnDestroy() {
this.unsubscribeCurrentRequest();
}
message(message) {
return this.ctx.localization.get(message);
}
cancelRequest() {
this.aiToolDirective.cancelRequest.emit();
this.unsubscribeCurrentRequest();
this.streaming = false;
}
onPromptRequest(ev) {
if (this.aiToolDirective.promptOutputs.length === 0) {
this.aiToolDirective.promptOutputs.push(this.loadingOutput);
}
this.unsubscribeCurrentRequest();
this.streaming = true;
this.activeView = 1;
if (ev.prompt) {
this.lastMessage = ev.prompt;
}
this.requestData = {
columns: this.columns,
promptMessage: ev.prompt,
url: this.requestUrl,
requestOptions: {
...this.requestOptions
}
};
if (!this.requestOptions.body) {
const requestBody = {
role: this.requestData.requestOptions.role,
contents: [
{
text: this.requestData.promptMessage
}
],
columns: this.requestData.columns
};
this.requestData.requestOptions.body = requestBody;
}
this.aiToolDirective.promptRequest.emit({ requestData: this.requestData, isRetry: ev.isRetry });
if (!this.requestUrl) {
return;
}
this.currentRequestSubscription = this.sendPromptRequest().subscribe((res) => {
if (res.body) {
this.processResponse(res);
this.streaming = false;
}
this.currentRequestSubscription = null;
}, (error) => {
this.handleError(error);
this.streaming = false;
this.currentRequestSubscription = null;
});
}
sendPromptRequest() {
const request = new HttpRequest(this.requestData.requestOptions.method, this.requestData.url, this.requestData.requestOptions.body, this.requestData.requestOptions);
return this.http.request(request);
}
processResponse(response) {
if (this.aiToolDirective.autoClose) {
this.aiToolDirective.emitOpenClose = true;
this.aiToolDirective.toggleWindow();
}
const responseBody = response.body;
const responseSuccessEvent = new GridToolbarAIResponseSuccessEvent(response);
this.aiToolDirective.responseSuccess.emit(responseSuccessEvent);
if (responseSuccessEvent.isDefaultPrevented()) {
this.deleteLoadingOutput();
return;
}
const isFilterable = Boolean(this.ctx.grid.filterable);
const isSortable = Boolean(this.ctx.grid.sortable);
const isGroupable = Boolean(this.ctx.grid.groupable);
if (isFilterable && responseBody.filter) {
this.processFilterResponse(responseBody.filter);
}
if (isSortable && responseBody.sort) {
this.processArrayResponse(responseBody.sort, this.ctx.grid.currentState.sort || [], (item) => item.field, (mergedArray) => this.ctx.grid.sortChange.next(mergedArray));
}
if (isGroupable && responseBody.group) {
this.processArrayResponse(responseBody.group, this.ctx.grid.currentState.group || [], (item) => item.field, (mergedArray) => this.ctx.grid.groupChange.next(mergedArray));
}
const responseContentStart = [`${this.ctx.localization.get('aiAssistantOutputCardBodyContent')} \n`];
const responseContentBody = responseBody.messages
.map((output, idx) => `${idx + 1} ${output}`)
.join('\n');
const output = {
id: this.idCounter++,
title: this.ctx.localization.get('aiAssistantOutputCardTitle'),
prompt: this.lastMessage,
output: responseContentStart.concat(responseContentBody).join(''),
};
this.deleteLoadingOutput();
this.aiToolDirective.promptOutputs.unshift(output);
}
handleError(error) {
const responseErrorEvent = new GridToolbarAIResponseErrorEvent(error);
this.aiToolDirective.responseError.emit(responseErrorEvent);
if (responseErrorEvent.isDefaultPrevented()) {
this.deleteLoadingOutput();
return;
}
const output = {
id: this.idCounter++,
prompt: this.lastMessage,
output: error.message
};
this.deleteLoadingOutput();
this.aiToolDirective.promptOutputs.unshift(output);
}
deleteLoadingOutput() {
if (this.aiToolDirective.promptOutputs[0]?.id === this.loadingOutput.id) {
this.aiToolDirective.promptOutputs.splice(0, 1);
}
}
unsubscribeCurrentRequest() {
if (this.currentRequestSubscription) {
this.currentRequestSubscription.unsubscribe();
this.currentRequestSubscription = null;
}
}
processArrayResponse(newItems, currentItems, getField, updateGrid) {
if (newItems?.length === 0) {
updateGrid([]);
}
else if (newItems?.length) {
let mergedArray = [...newItems];
const newFields = newItems.map(getField);
const existingItemsToKeep = currentItems.filter(item => !newFields.includes(getField(item)));
mergedArray = [...mergedArray, ...existingItemsToKeep];
updateGrid(mergedArray);
}
}
processFilterResponse(filter) {
const processedFilter = convertDateStringsInFilter(filter, this.columnInfoService.leafNamedColumns);
const clearFilter = Object.keys(processedFilter).length === 0;
if (clearFilter) {
this.ctx.grid.filterChange.next(undefined);
}
else if (processedFilter?.filters.length) {
const currentFilter = this.ctx.grid.currentState.filter;
let mergedFilter = processedFilter;
if (currentFilter && currentFilter.filters?.length > 0) {
mergedFilter = {
logic: 'and',
filters: [
currentFilter,
processedFilter
]
};
}
this.ctx.grid.filterChange.next(mergedFilter);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AiAssistantComponent, deps: [{ token: i1.HttpClient }, { token: i2.ContextService }, { token: i3.ColumnInfoService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: AiAssistantComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "aiPrompt", first: true, predicate: AIPromptComponent, descendants: true }], ngImport: i0, template: `
<kendo-aiprompt
#aiPrompt
[promptSuggestions]="aiPromptSettings?.promptSuggestions"
[showOutputRating]="aiPromptSettings?.showOutputRating"
[streaming]="streaming"
[speechToTextButton]="aiPromptSettings?.speechToTextButton"
[(activeView)]="activeView"
[generateButtonSVGIcon]="aiPromptSettings?.generateButtonSVGIcon"
[generateButtonIcon]="aiPromptSettings?.generateButtonIcon"
[disabledGenerateButton]="disabledGenerateButton || promptView.textAreaValue?.length === 0"
[promptOutputs]="aiPromptSettings?.promptOutputs"
[textAreaSettings]="aiPromptSettings?.textAreaSettings"
(promptRequest)="onPromptRequest($event)"
(promptRequestCancel)="cancelRequest()"
>
<kendo-aiprompt-prompt-view #promptView></kendo-aiprompt-prompt-view>
<kendo-aiprompt-output-view></kendo-aiprompt-output-view>
<ng-template *ngIf="streaming && aiPrompt.streaming" kendoAIPromptOutputTemplate let-output>
<div class="k-card">
<div class="k-card-header">
<div class="k-card-title">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" [style.width.px]="200"></span>
</div>
<div class="k-card-subtitle">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
<div class="k-card-body">
<span class="k-skeleton k-skeleton-rect k-skeleton-pulse" style="height: 80px;"></span>
</div>
<div class="k-card-actions">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
</ng-template>
<ng-template *ngIf="!(streaming && aiPrompt.streaming)" kendoAIPromptOutputBodyTemplate let-output>
<p>{{output.output}}</p>
</ng-template>
<kendo-aiprompt-messages
[generateOutput]="message('aiAssistantApplyButtonText')"
></kendo-aiprompt-messages>
</kendo-aiprompt>
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AIPromptComponent, selector: "kendo-aiprompt", inputs: ["activeView", "promptCommands", "promptSuggestions", "promptOutputs", "showOutputRating", "streaming", "speechToTextButton", "textAreaSettings", "generateButtonSVGIcon", "generateButtonIcon", "disabledGenerateButton"], outputs: ["activeViewChange", "promptRequest", "commandExecute", "outputCopy", "outputRatingChange", "promptRequestCancel"], exportAs: ["kendoAIPrompt"] }, { kind: "component", type: AIPromptCustomMessagesComponent, selector: "kendo-aiprompt-messages" }, { kind: "component", type: PromptViewComponent, selector: "kendo-aiprompt-prompt-view" }, { kind: "component", type: OutputViewComponent, selector: "kendo-aiprompt-output-view" }, { kind: "directive", type: AIPromptOutputTemplateDirective, selector: "[kendoAIPromptOutputTemplate]" }, { kind: "directive", type: AIPromptOutputBodyTemplateDirective, selector: "[kendoAIPromptOutputBodyTemplate]" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AiAssistantComponent, decorators: [{
type: Component,
args: [{
standalone: true,
imports: [NgIf, AIPromptComponent, AIPromptCustomMessagesComponent, PromptViewComponent, OutputViewComponent, AIPromptOutputTemplateDirective, AIPromptOutputBodyTemplateDirective],
template: `
<kendo-aiprompt
#aiPrompt
[promptSuggestions]="aiPromptSettings?.promptSuggestions"
[showOutputRating]="aiPromptSettings?.showOutputRating"
[streaming]="streaming"
[speechToTextButton]="aiPromptSettings?.speechToTextButton"
[(activeView)]="activeView"
[generateButtonSVGIcon]="aiPromptSettings?.generateButtonSVGIcon"
[generateButtonIcon]="aiPromptSettings?.generateButtonIcon"
[disabledGenerateButton]="disabledGenerateButton || promptView.textAreaValue?.length === 0"
[promptOutputs]="aiPromptSettings?.promptOutputs"
[textAreaSettings]="aiPromptSettings?.textAreaSettings"
(promptRequest)="onPromptRequest($event)"
(promptRequestCancel)="cancelRequest()"
>
<kendo-aiprompt-prompt-view #promptView></kendo-aiprompt-prompt-view>
<kendo-aiprompt-output-view></kendo-aiprompt-output-view>
<ng-template *ngIf="streaming && aiPrompt.streaming" kendoAIPromptOutputTemplate let-output>
<div class="k-card">
<div class="k-card-header">
<div class="k-card-title">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" [style.width.px]="200"></span>
</div>
<div class="k-card-subtitle">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
<div class="k-card-body">
<span class="k-skeleton k-skeleton-rect k-skeleton-pulse" style="height: 80px;"></span>
</div>
<div class="k-card-actions">
<span class="k-skeleton k-skeleton-text k-skeleton-pulse" style="width: 100%;"></span>
</div>
</div>
</ng-template>
<ng-template *ngIf="!(streaming && aiPrompt.streaming)" kendoAIPromptOutputBodyTemplate let-output>
<p>{{output.output}}</p>
</ng-template>
<kendo-aiprompt-messages
[generateOutput]="message('aiAssistantApplyButtonText')"
></kendo-aiprompt-messages>
</kendo-aiprompt>
`
}]
}], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: i2.ContextService }, { type: i3.ColumnInfoService }]; }, propDecorators: { aiPrompt: [{
type: ViewChild,
args: [AIPromptComponent]
}] } });