Skip to content

Commit

Permalink
Merge pull request #16 from xpert-ai/develop
Browse files Browse the repository at this point in the history
version 3.0.7
  • Loading branch information
tiven-w authored Dec 24, 2024
2 parents b3fc13d + 97c67c1 commit 93b8c8b
Show file tree
Hide file tree
Showing 91 changed files with 1,369 additions and 145 deletions.
32 changes: 16 additions & 16 deletions apps/api/src/plugin-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,22 @@ function getDbConfig(): ConnectionOptions {
};
}

case 'sqlite': {
const sqlitePath =
process.env.DB_PATH ||
path.join(
path.resolve('.', ...['apps', 'api', 'data']),
'gauzy.sqlite3'
);
// case 'sqlite': {
// const sqlitePath =
// process.env.DB_PATH ||
// path.join(
// path.resolve('.', ...['apps', 'api', 'data']),
// 'xxxx.sqlite3'
// );

return {
type: dbType,
database: sqlitePath,
logging: true,
// Removes console logging, instead logs all queries in a file ormlogs.log
logger: 'file',
synchronize: true
};
}
// return {
// type: dbType,
// database: sqlitePath,
// logging: true,
// // Removes console logging, instead logs all queries in a file ormlogs.log
// logger: 'file',
// synchronize: true
// };
// }
}
}
8 changes: 7 additions & 1 deletion apps/cloud/src/app/@core/services/xpert.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { toParams } from '@metad/ocap-angular/core'
import { NGXLogger } from 'ngx-logger'
import { BehaviorSubject, tap } from 'rxjs'
import { API_XPERT_ROLE } from '../constants/app.constants'
import { ICopilotStore, IUser, IXpert, IXpertAgentExecution, OrderTypeEnum, TChatRequest, TDeleteResult, TXpertTeamDraft, XpertTypeEnum } from '../types'
import { ICopilotStore, IIntegration, IUser, IXpert, IXpertAgentExecution, OrderTypeEnum, TChatRequest, TDeleteResult, TXpertTeamDraft, XpertTypeEnum } from '../types'
import { XpertWorkspaceBaseCrudService } from './xpert-workspace.service'
import { injectApiBaseUrl } from '../providers'
import { injectFetchEventSource } from './fetch-event-source'
Expand Down Expand Up @@ -57,6 +57,12 @@ export class XpertService extends XpertWorkspaceBaseCrudService<IXpert> {
publish(id: string) {
return this.httpClient.post<IXpert>(this.apiBaseUrl + `/${id}/publish`, {})
}
publishIntegration(id: string, integration: Partial<IIntegration>) {
return this.httpClient.post<IIntegration>(this.apiBaseUrl + `/${id}/publish/integration`, integration)
}
removeIntegration(xpertId: string, id: string) {
return this.httpClient.delete(this.apiBaseUrl + `/${xpertId}/publish/integration/${id}`,)
}

validateTitle(title: string) {
return this.httpClient.get<IXpert[]>(this.apiBaseUrl + `/validate`, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common'
import { booleanAttribute, Component, computed, HostListener, HostBinding, inject, input, model } from '@angular/core'
import { booleanAttribute, Component, computed, HostListener, HostBinding, inject, input, model, effect } from '@angular/core'
import { MatDialog } from '@angular/material/dialog'
import { EmojiComponent } from '@ctrl/ngx-emoji-mart/ngx-emoji'
import { NgxControlValueAccessor } from 'ngxtension/control-value-accessor'
Expand Down Expand Up @@ -70,6 +70,14 @@ export class EmojiAvatarComponent {

@HostBinding('class.focused') focused = false;

constructor() {
effect(() => {
if (this.cva.value$()) {
this.avatar.set(this.cva.value$())
}
}, { allowSignalWrites: true })
}

@HostListener('click')
onClick() {
if (this.editable()) {
Expand Down
3 changes: 2 additions & 1 deletion apps/cloud/src/app/@shared/integration/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './integration-list/list.component'
export * from './integration-list/list.component'
export * from './integration-form/integration.component'
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<form [formGroup]="formGroup" class="w-full grow p-8 overflow-y-auto flex flex-col gap-2">
<emoji-avatar formControlName="avatar" large editable class="self-center rounded-xl overflow-hidden shadow-lg"/>

<ngm-input class="flex-1" [label]="'PAC.KEY_WORDS.Name' | translate: {Default: 'Name'}"
formControlName="name"
/>

<div class="flex-1 min-w-full flex flex-col">
<label class="ngm-input-label shrink-0">{{'PAC.KEY_WORDS.Description' | translate: {Default: 'Description'} }}</label>
<textarea class="ngm-input-element" matInput formControlName="description"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cdkAutosizeMaxRows="5">
</textarea>
</div>

@if (schema(); as fields) {
<formly-form [form]="optionsForm"
[options]="formOptions"
[fields]="fields"
[model]="optionsModel"
/>
} @else if (integrationProvider()) {
<list-content-loader />
}

@if(webhookUrl()) {
<div>
<p>{{ 'PAC.Integration.WebhookUrl' | translate: {Default: 'Webhook url'} }}:</p>
<pre class="px-2 py-1 rounded-md text-sm whitespace-break-spaces bg-gray-50 dark:bg-white/10">{{webhookUrl()}}</pre>
</div>
}

<div class="w-full flex justify-start">
<button type="button" class="btn disabled:btn-disabled btn-secondary btn-medium"
(click)="test()">{{ 'PAC.KEY_WORDS.Test' | translate: {Default: 'Test'} }}</button>
</div>
</form>

@if (loading()) {
<ngm-spin class="absolute left-0 top-0 w-full h-full" />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
@apply relative;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { CdkListboxModule } from '@angular/cdk/listbox'
import { TextFieldModule } from '@angular/cdk/text-field'
import { CommonModule } from '@angular/common'
import { Component, computed, effect, inject, model, signal } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'
import { MatInputModule } from '@angular/material/input'
import { NgmInputComponent, NgmSpinComponent } from '@metad/ocap-angular/common'
import { NgmI18nPipe } from '@metad/ocap-angular/core'
import { ContentLoaderModule } from '@ngneat/content-loader'
import { FormlyModule } from '@ngx-formly/core'
import { TranslateModule } from '@ngx-translate/core'
import { assign, omit } from 'lodash-es'
import { map, startWith } from 'rxjs'
import { injectApiBaseUrl, injectToastr, IntegrationService, toFormlySchema } from '../../../@core'
import { getErrorMessage, IIntegration, INTEGRATION_PROVIDERS } from '../../../@core/types'
import { EmojiAvatarComponent } from '../../avatar'

@Component({
standalone: true,
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
CdkListboxModule,
TextFieldModule,
TranslateModule,
ContentLoaderModule,
FormlyModule,
MatInputModule,
EmojiAvatarComponent,
NgmInputComponent,
NgmSpinComponent
],
selector: 'pac-integration-form',
templateUrl: 'integration.component.html',
styleUrls: ['integration.component.scss']
})
export class IntegrationFormComponent {
readonly integrationService = inject(IntegrationService)
readonly #toastr = injectToastr()
readonly apiBaseUrl = injectApiBaseUrl()
readonly i18n = new NgmI18nPipe()

readonly integration = model<IIntegration>()

readonly formGroup = new FormGroup({
id: new FormControl(null),
name: new FormControl(null, [Validators.required]),
avatar: new FormControl(null),
description: new FormControl(null),
slug: new FormControl(null),
provider: new FormControl(null),
options: new FormGroup({})
})

get optionsForm() {
return this.formGroup.get('options') as FormGroup
}

optionsModel = {}
formOptions = {}

readonly providers = signal(
Object.keys(INTEGRATION_PROVIDERS).map((name) => ({
key: name,
caption: this.i18n.transform(INTEGRATION_PROVIDERS[name].label)
}))
)
readonly provider = this.formGroup.get('provider')
readonly integrationProvider = toSignal(
this.provider.valueChanges.pipe(
startWith(this.provider.value),
map((provider) => INTEGRATION_PROVIDERS[provider])
)
)

readonly schema = computed(() => {
const schema = this.integrationProvider()?.schema
return schema
? toFormlySchema(
{
...schema,
properties: omit(schema.properties, 'xpertId')
},
this.i18n
)
: null
})

readonly webhookUrl = computed(() =>
this.integration() ? this.integrationProvider()?.webhookUrl(this.integration(), this.apiBaseUrl) : null
)

readonly loading = signal(false)

constructor() {
effect(
() => {
if (this.integration()) {
this.formGroup.patchValue(this.integration())
assign(this.optionsModel, this.integration().options)
if (this.integration().id) {
this.formGroup.markAsPristine()
} else {
this.formGroup.markAsDirty()
}
}
},
{ allowSignalWrites: true }
)
}

compareId(a: IIntegration, b: IIntegration): boolean {
return a?.id === b?.id
}

getProvider(integration?: IIntegration) {
return INTEGRATION_PROVIDERS[integration.name]
}

onModelChange(model) {
console.log(model)
this.integration.set(model)
}

test() {
this.loading.set(true)
this.integrationService.test({ ...this.formGroup.value }).subscribe({
next: (result) => {
this.formGroup.patchValue(result)
this.formGroup.markAsDirty()
this.loading.set(false)
this.#toastr.success('PAC.Messages.Successfully', { Default: 'Successfully!' })
},
error: (error) => {
this.#toastr.danger(getErrorMessage(error))
this.loading.set(false)
}
})
}
}
3 changes: 2 additions & 1 deletion apps/cloud/src/app/@shared/xpert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export * from './tool-name-input/input.component'
export * from './xpert-card/xpert-card.component'
export * from './execution-status/execution.component'
export * from './execution-accordion/execution.component'
export * from './tool-call-confirm/confirm.component'
export * from './tool-call-confirm/confirm.component'
export * from './publish/publish.component'
94 changes: 94 additions & 0 deletions apps/cloud/src/app/@shared/xpert/publish/publish.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<div class="ngm-theme-dark dark w-[300px] shrink-0 flex flex-col justify-start overflow-auto bg-bluegray-700 text-white p-4 group">
<div class="w-full flex justify-start items-center mb-4" cdkDrag cdkDragRootElement=".cdk-overlay-pane" cdkDragHandle>
<span class="text-lg pointer-events-none">
{{ 'PAC.Xpert.PublishThirdPlatforms' | translate: { Default: 'Publish to third-party platforms' } }}
</span>
</div>

<ul class="flex flex-col items-stretch gap-1">
@for (item of integrations(); track item.id) {
<li class="integration flex justify-start items-center p-2 rounded-lg cursor-pointer bg-gray-50/10 hover:bg-gray-50/50 text-text-primary"
[class.active]="item.id === integration()?.id"
(click)="selectIntegration(item)"
>
<emoji-avatar small class="shrink-0 rounded-xl overflow-hidden shadow-sm mr-1"
[avatar]="item.avatar" />
{{ item.name }}
</li>
}
</ul>

<div class="flex justify-end mt-2">
<button type="button" class="btn btn-primary btn-large pressable"
[cdkMenuTriggerFor]="addMenu"
>
<i class="ri-apps-2-add-line"></i> {{ 'PAC.ACTIONS.Add' | translate: {Default: 'Add'} }}
</button>
</div>
</div>

<ng-template #addMenu>
<div cdkMenu class="w-[200px] divide-y-2 p-2">
@for (provider of providers(); track provider.name) {
<div cdkMenuItem class="mb-1 p-2 relative rounded-xl"
[class.enterprise-pro]="provider.pro"
(click)="addIntegration(provider)"
>
<img [src]="'/assets/images/destinations/' + provider.avatar" class="shrink-0 w-auto h-6" >
{{ provider.label | i18n }}

@if (provider.pro) {
<img src="/assets/images/illustrations/pro.svg" class="absolute -right-1 -top-1 w-5 h-5"
[matTooltip]="'PAC.KEY_WORDS.AvailablePro' | translate: {Default: 'Available in enterprise pro edition'}"
matTooltipPosition="above"
>
}
</div>
}
</div>
</ng-template>

<div class="grow relative flex flex-col w-[600px] overflow-auto">
<div class="!border-b-black/5 shrink-0 border-b border-b-gray-100 py-4 sticky top-0 z-10 bg-slate-50"
cdkDrag cdkDragRootElement=".cdk-overlay-pane" cdkDragHandle>
<div class="flex justify-between items-center pl-6 pr-5 h-6">
<div>{{ 'PAC.Xpert.PublishTo' | translate: { Default: 'Publish to ' } }} {{integration()?.provider || '?'}}</div>
<div class="flex items-center">
<div class="flex justify-center items-center w-6 h-6 cursor-pointer" (click)="close()">
<i class="ri-close-line"></i>
</div>
</div>
</div>
</div>

@for (integration of selectedIntegrations(); track integration) {
<pac-integration-form #form [integration]="integration" (integrationChange)="updateIntegration($event)" />

<div class="mt-2 shrink-0 flex py-4 px-6 rounded-b-[10px] bg-gray-50 border-t border-black/5">

@if (integration.id) {
<button type="button" class="btn disabled:btn-disabled btn-danger btn-medium"
(click)="remove(integration)">{{'PAC.ACTIONS.Remove' | translate: {Default: 'Remove'} }}</button>
}

<span class="grow"></span>

<div class="flex space-x-2">
<button type="button" class="btn disabled:btn-disabled btn-secondary btn-medium"
(click)="cancel()"
>
{{ 'PAC.ACTIONS.Cancel' | translate: {Default: 'Cancel'} }}
</button>

<button type="button" class="btn disabled:btn-disabled btn-primary btn-medium"
[disabled]="form.formGroup.invalid || form.formGroup.pristine"
(click)="save(form.formGroup.value)"
>{{ 'PAC.ACTIONS.Save' | translate: {Default: 'Save'} }}</button>
</div>
</div>
}

@if (loading()) {
<ngm-spin class="absolute left-0 top-0 w-full h-full"></ngm-spin>
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
:host {
@apply flex h-[80vh] rounded-2xl overflow-hidden shadow-xl bg-components-card-bg;
}

.integration.active {
@apply bg-white/30;
}
Loading

0 comments on commit 93b8c8b

Please sign in to comment.