Skip to content

Commit

Permalink
Merge pull request #8 from xpert-ai/develop
Browse files Browse the repository at this point in the history
Feat: workspace general page & npm action
  • Loading branch information
tiven-w authored Dec 5, 2024
2 parents a690a1e + b5b4d31 commit 9d64939
Show file tree
Hide file tree
Showing 25 changed files with 758 additions and 443 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker-build-publish-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,4 @@ jobs:

- name: Push to Aliyun Registry
run: |
docker push registry.cn-hangzhou.aliyuncs.com/metad/xpert-webapp-demo:latest
docker push registry.cn-hangzhou.aliyuncs.com/metad/xpert-webapp-demo:latest
6 changes: 2 additions & 4 deletions .github/workflows/publish-npm-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ jobs:
publish:
runs-on: ubuntu-latest
if: github.event_name == 'push' && !contains(github.ref, 'beta')
environment:
name: prod
environment: production
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -55,8 +54,7 @@ jobs:
publish-beta:
runs-on: ubuntu-latest
if: github.event_name == 'push' && contains(github.ref, 'beta')
environment:
name: prod
environment: production
steps:
- uses: actions/checkout@v4
with:
Expand Down
14 changes: 11 additions & 3 deletions apps/cloud/src/app/@core/services/xpert-workspace.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ export class XpertWorkspaceService extends OrganizationBaseCrudService<IXpertWor
super(API_XPERT_WORKSPACE)
}

getAllMy() {
getAllMy(params?: PaginationParams<IXpertWorkspace>) {
return this.selectOrganizationId().pipe(
switchMap(() =>
this.#refresh.pipe(switchMap(() => this.httpClient.get<{ items: IXpertWorkspace[] }>(this.apiBaseUrl + `/my`)))
this.#refresh.pipe(
switchMap(() =>
this.httpClient.get<{ items: IXpertWorkspace[] }>(this.apiBaseUrl + `/my`, { params: toHttpParams(params) })
)
)
)
)
}
Expand All @@ -31,6 +35,10 @@ export class XpertWorkspaceService extends OrganizationBaseCrudService<IXpertWor
return this.httpClient.put<IXpertWorkspace>(this.apiBaseUrl + `/${id}/members`, members)
}

archive(id: string) {
return this.httpClient.post<IXpertWorkspace>(this.apiBaseUrl + `/${id}/archive`, {})
}

refresh() {
this.#refresh.next()
}
Expand All @@ -50,4 +58,4 @@ export class XpertWorkspaceBaseCrudService<T> extends OrganizationBaseCrudServic

export function injectWorkspaceService() {
return inject(XpertWorkspaceService)
}
}
14 changes: 12 additions & 2 deletions apps/cloud/src/app/features/xpert/workspace/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
injectTags,
injectUser,
ITag,
OrderTypeEnum,
routeAnimations,
TagCategoryEnum,
ToastrService,
Expand Down Expand Up @@ -99,7 +100,11 @@ export class XpertWorkspaceHomeComponent {
readonly isMobile = this.appService.isMobile
readonly lang = this.appService.lang

readonly workspaces = toSignal(this.workspaceService.getAllMy().pipe(map(({ items }) => items)), {initialValue: null})
readonly workspaces = toSignal(
this.workspaceService.getAllMy({ order: { updatedAt: OrderTypeEnum.DESC } })
.pipe(map(({ items }) => items)),
{initialValue: null}
)
readonly selectedWorkspaces = model<string[]>([])
readonly workspace = computed(() => this.workspaces()?.find((_) => _.id === this.selectedWorkspaces()[0]), {
equal: (a, b) => a?.id === b?.id
Expand Down Expand Up @@ -187,6 +192,11 @@ export class XpertWorkspaceHomeComponent {
}
})
.afterClosed()
.subscribe()
.subscribe((event) => {
if (event === 'deleted' || event === 'archived') {
this.workspaceService.refresh()
this.router.navigate(['/xpert/w'])
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<div class="sticky top-0 px-6 py-4 flex items-center h-14 mb-4 bg-white text-base font-medium text-gray-900 z-20">
<div class="shrink-0 text-xl">{{'PAC.Xpert.General' | translate: {Default: 'General'} }}</div>
<div class="grow flex justify-end">
</div>
</div>

<div class="relative flex-1 flex flex-col justify-between items-stretch px-8 py-4">
<div class="pt-2">
<div class="pb-2">
<label class="py-2 font-medium leading-[20px] text-gray-900">{{ 'PAC.Xpert.Name' | translate: {Default: 'Name'} }}</label>
<input class="w-full h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg border border-transparent outline-none appearance-none caret-primary-600 placeholder:text-gray-400 hover:bg-gray-50 hover:border hover:border-gray-300 focus:bg-gray-50 focus:border focus:border-gray-300 focus:shadow-xs"
[placeholder]="'PAC.Xpert.EnterWorkspaceName' | translate: {Default: 'Enter workspace name'}"
[(ngModel)]="name"
>
</div>
<button type="button" class="btn btn-large btn-primary pressable" (click)="update()">
{{ 'PAC.ACTIONS.Update' | translate: {Default: 'Update'} }}
</button>
</div>

<div>
<div class="text-xl font-semibold text-orange-500 pb-2">
{{ 'PAC.KEY_WORDS.DangerZone' | translate: {Default: 'Danger zone'} }}
</div>

<div class="border border-solid border-orange-500 rounded-xl flex flex-col">
<div class="w-full flex justify-between items-center p-4">
<div class="flex flex-col">
<div class="font-semibold">
{{ 'PAC.Xpert.ArchiveWorkspace' | translate: {Default: 'Archive this workspace'} }}
</div>
<div>
{{ 'PAC.Xpert.ArchiveWorkspaceDesc' | translate: {Default: 'Mark this workspace and all its xperts and toolsets as archived and read-only'} }}.
</div>
</div>

<button type="button" class="btn btn-large danger pressable" (click)="archive()">
{{ 'PAC.Xpert.ArchiveWorkspace' | translate: {Default: 'Archive this workspace'} }}
</button>
</div>

<div class="border-b border-solid border-divider-regular"></div>

<div class="w-full flex justify-between items-center p-4">
<div class="flex flex-col">
<div class="font-semibold">
{{ 'PAC.Xpert.DeleteWorkspace' | translate: {Default: 'Delete this workspace'} }}
</div>
<div>
{{ 'PAC.Xpert.DeleteWorkspaceDesc' | translate: {Default: 'Once deleted, it will be gone forever. Please be certain'} }}.
</div>
</div>

<button type="button" class="btn btn-large danger pressable" (click)="delete()">
{{ 'PAC.Xpert.DeleteWorkspace' | translate: {Default: 'Delete this workspace'} }}
</button>
</div>
</div>
</div>

@if (loading()) {
<ngm-spin class="absolute top-0 left-0 w-full h-full"></ngm-spin>
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
@apply flex flex-col;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { CdkListboxModule } from '@angular/cdk/listbox'
import { CommonModule } from '@angular/common'
import { Component, effect, input, model, output, signal } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { NgmSpinComponent } from '@metad/ocap-angular/common'
import { TranslateModule } from '@ngx-translate/core'
import {
getErrorMessage,
IfAnimation,
injectToastr,
injectWorkspaceService,
IXpertWorkspace
} from 'apps/cloud/src/app/@core'

@Component({
selector: 'xpert-workspace-settings-general',
standalone: true,
imports: [CommonModule, FormsModule, CdkListboxModule, TranslateModule, NgmSpinComponent],
templateUrl: './general.component.html',
styleUrl: './general.component.scss',
animations: [IfAnimation]
})
export class XpertWorkspaceSettingsGeneralComponent {
readonly workspaceService = injectWorkspaceService()
readonly #toastr = injectToastr()

// Inputs
readonly workspace = input<IXpertWorkspace>()

// Outputs
readonly deleted = output()
readonly archived = output()

readonly name = model<string>()

readonly loading = signal(false)

constructor() {
effect(
() => {
if (this.workspace()) {
this.name.set(this.workspace().name)
}
},
{ allowSignalWrites: true }
)
}

update() {
this.loading.set(true)
this.workspaceService
.update(this.workspace().id, {
name: this.name()
})
.subscribe({
next: () => {
this.loading.set(false)
this.#toastr.success('PAC.Messages.UpdatedSuccessfully', { Default: 'Updated successfully' })
},
error: (error) => {
this.loading.set(false)
this.#toastr.error(getErrorMessage(error))
}
})
}

archive() {
this.loading.set(true)
this.workspaceService.archive(this.workspace().id)
.subscribe({
next: () => {
this.loading.set(false)
this.archived.emit()
this.#toastr.success('PAC.Messages.ArchivedSuccessfully', { Default: 'Archived successfully' })
},
error: (error) => {
this.loading.set(false)
this.#toastr.error(getErrorMessage(error))
}
})
}

delete() {
this.loading.set(true)
this.workspaceService.delete(this.workspace().id)
.subscribe({
next: () => {
this.loading.set(false)
this.deleted.emit()
this.#toastr.success('PAC.Messages.DeletedSuccessfully', { Default: 'Deleted successfully' })
},
error: (error) => {
this.loading.set(false)
this.#toastr.error(getErrorMessage(error))
}
})
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<div class="sticky top-0 px-6 py-4 flex items-center h-14 mb-4 bg-white text-base font-medium text-gray-900 z-20">
<div class="shrink-0">{{'PAC.Xpert.Members' | translate: {Default: 'Members'} }}</div>
<div class="shrink-0 text-xl">{{'PAC.Xpert.Members' | translate: {Default: 'Members'} }}</div>
<div class="grow flex justify-end">

</div>
</div>

<div class="px-4 sm:px-8 pt-2">
<div class="flex flex-col">
<div class="flex items-center mb-4 p-3 bg-gray-50 rounded-2xl">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { injectUser, IUser, IXpertWorkspace, XpertWorkspaceService } from 'apps/
import { UserPipe, UserRoleSelectComponent } from 'apps/cloud/src/app/@shared'
import { uniqBy } from 'lodash-es'
import { EMPTY, filter, switchMap } from 'rxjs'
import { UserProfileInlineComponent } from '../../../../@shared/'
import { UserProfileInlineComponent } from '../../../../../@shared/'

@Component({
selector: 'xpert-workspace-members',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
<div class="mb-4">
<div class="px-2 mb-[6px] text-[10px] sm:text-sm font-medium uppercase text-gray-500">{{ 'PAC.KEY_WORDS.Workspace' | translate: {Default: 'Workspace'} }}</div>
<ul cdkListbox [(ngModel)]="selectedMenus">
<li
class="menu-item"
[title]="'PAC.Xpert.General' | translate: {Default: 'General'}"
[cdkOption]="'general'"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
class="w-5 h-5 ml-3 mr-2" >
<path d="M2 11.9998C2 11.1353 2.1097 10.2964 2.31595 9.49631C3.40622 9.55283 4.48848 9.01015 5.0718 7.99982C5.65467 6.99025 5.58406 5.78271 4.99121 4.86701C6.18354 3.69529 7.66832 2.82022 9.32603 2.36133C9.8222 3.33385 10.8333 3.99982 12 3.99982C13.1667 3.99982 14.1778 3.33385 14.674 2.36133C16.3317 2.82022 17.8165 3.69529 19.0088 4.86701C18.4159 5.78271 18.3453 6.99025 18.9282 7.99982C19.5115 9.01015 20.5938 9.55283 21.6841 9.49631C21.8903 10.2964 22 11.1353 22 11.9998C22 12.8643 21.8903 13.7032 21.6841 14.5033C20.5938 14.4468 19.5115 14.9895 18.9282 15.9998C18.3453 17.0094 18.4159 18.2169 19.0088 19.1326C17.8165 20.3043 16.3317 21.1794 14.674 21.6383C14.1778 20.6658 13.1667 19.9998 12 19.9998C10.8333 19.9998 9.8222 20.6658 9.32603 21.6383C7.66832 21.1794 6.18354 20.3043 4.99121 19.1326C5.58406 18.2169 5.65467 17.0094 5.0718 15.9998C4.48848 14.9895 3.40622 14.4468 2.31595 14.5033C2.1097 13.7032 2 12.8643 2 11.9998ZM6.80385 14.9998C7.43395 16.0912 7.61458 17.3459 7.36818 18.5236C7.77597 18.8138 8.21005 19.0652 8.66489 19.2741C9.56176 18.4712 10.7392 17.9998 12 17.9998C13.2608 17.9998 14.4382 18.4712 15.3351 19.2741C15.7899 19.0652 16.224 18.8138 16.6318 18.5236C16.3854 17.3459 16.566 16.0912 17.1962 14.9998C17.8262 13.9085 18.8225 13.1248 19.9655 12.7493C19.9884 12.5015 20 12.2516 20 11.9998C20 11.7481 19.9884 11.4981 19.9655 11.2504C18.8225 10.8749 17.8262 10.0912 17.1962 8.99982C16.566 7.90845 16.3854 6.65378 16.6318 5.47605C16.224 5.18588 15.7899 4.93447 15.3351 4.72552C14.4382 5.52844 13.2608 5.99982 12 5.99982C10.7392 5.99982 9.56176 5.52844 8.66489 4.72552C8.21005 4.93447 7.77597 5.18588 7.36818 5.47605C7.61458 6.65378 7.43395 7.90845 6.80385 8.99982C6.17376 10.0912 5.17754 10.8749 4.03451 11.2504C4.01157 11.4981 4 11.7481 4 11.9998C4 12.2516 4.01157 12.5015 4.03451 12.7493C5.17754 13.1248 6.17376 13.9085 6.80385 14.9998ZM12 14.9998C10.3431 14.9998 9 13.6567 9 11.9998C9 10.343 10.3431 8.99982 12 8.99982C13.6569 8.99982 15 10.343 15 11.9998C15 13.6567 13.6569 14.9998 12 14.9998ZM12 12.9998C12.5523 12.9998 13 12.5521 13 11.9998C13 11.4475 12.5523 10.9998 12 10.9998C11.4477 10.9998 11 11.4475 11 11.9998C11 12.5521 11.4477 12.9998 12 12.9998Z"></path>
</svg>
<div class="truncate">{{ 'PAC.Xpert.General' | translate: {Default: 'General'} }}</div>
</li>

<li
class="menu-item"
[title]="'PAC.Xpert.ModelProviders' | translate: {Default: 'Model Providers'}"
Expand All @@ -18,7 +30,7 @@
width="24"
height="24"
fill="currentColor"
class="remixicon w-4 h-4 ml-3 mr-2"
class="w-5 h-5 ml-3 mr-2"
>
<path
d="M12 1L21.5 6.5V17.5L12 23L2.5 17.5V6.5L12 1ZM5.49388 7.0777L12.0001 10.8444L18.5062 7.07774L12 3.311L5.49388 7.0777ZM4.5 8.81329V16.3469L11.0001 20.1101V12.5765L4.5 8.81329ZM13.0001 20.11L19.5 16.3469V8.81337L13.0001 12.5765V20.11Z"
Expand All @@ -38,7 +50,7 @@
width="24"
height="24"
fill="currentColor"
class="remixicon w-4 h-4 ml-3 mr-2"
class="w-5 h-5 ml-3 mr-2"
>
<path
d="M10 19.748V16.4C10 15.1174 10.9948 14.1076 12.4667 13.5321C11.5431 13.188 10.5435 13 9.5 13C7.61013 13 5.86432 13.6168 4.45286 14.66C5.33199 17.1544 7.41273 19.082 10 19.748ZM18.8794 16.0859C18.4862 15.5526 17.1708 15 15.5 15C13.4939 15 12 15.7967 12 16.4V20C14.9255 20 17.4843 18.4296 18.8794 16.0859ZM9.55 11.5C10.7926 11.5 11.8 10.4926 11.8 9.25C11.8 8.00736 10.7926 7 9.55 7C8.30736 7 7.3 8.00736 7.3 9.25C7.3 10.4926 8.30736 11.5 9.55 11.5ZM15.5 12.5C16.6046 12.5 17.5 11.6046 17.5 10.5C17.5 9.39543 16.6046 8.5 15.5 8.5C14.3954 8.5 13.5 9.39543 13.5 10.5C13.5 11.6046 14.3954 12.5 15.5 12.5ZM12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22Z"
Expand All @@ -55,6 +67,11 @@

<div class="relative w-[824px] h-[720px] pb-4 overflow-y-auto">
@switch(menu()) {
@case('general') {
<xpert-workspace-settings-general @ifAnimationTrigger [workspace]="workspace()" class="h-full"
(deleted)="onDeleted()"
(archived)="onArchived()" />
}
@case('models') {
<xpert-workspace-models @ifAnimationTrigger [workspace]="workspace()" />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ import { Component, computed, inject, model, signal } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { IfAnimation, XpertWorkspaceService } from 'apps/cloud/src/app/@core';
import { derivedAsync } from 'ngxtension/derived-async';
import { XpertWorkspaceMembersComponent } from '../members/members.component';
import { XpertWorkspaceMembersComponent } from './members/members.component';
import { CdkListboxModule } from '@angular/cdk/listbox';
import { FormsModule } from '@angular/forms';
import { XpertWorkspaceModelsComponent } from '../models/models.component';
import { XpertWorkspaceModelsComponent } from './models/models.component';
import { TranslateModule } from '@ngx-translate/core';
import { XpertWorkspaceSettingsGeneralComponent } from './general/general.component';

@Component({
selector: 'xpert-workspace-settings',
standalone: true,
imports: [CommonModule, FormsModule, CdkListboxModule, TranslateModule, XpertWorkspaceModelsComponent, XpertWorkspaceMembersComponent],
imports: [CommonModule, FormsModule, CdkListboxModule, TranslateModule,
XpertWorkspaceModelsComponent, XpertWorkspaceMembersComponent,
XpertWorkspaceSettingsGeneralComponent],
templateUrl: './settings.component.html',
styleUrl: './settings.component.scss',
animations: [IfAnimation]
Expand All @@ -31,10 +34,18 @@ export class XpertWorkspaceSettingsComponent {

readonly owner = computed(() => this.workspace()?.owner)

readonly selectedMenus = model<Array<'models' | 'members'>>(['members'])
readonly selectedMenus = model<Array<'general' | 'models' | 'members'>>(['general'])
readonly menu = computed(() => this.selectedMenus()[0])

close() {
this.#dialogRef.close()
close(reason?: string) {
this.#dialogRef.close(reason)
}

onDeleted() {
this.close('deleted')
}

onArchived() {
this.close('archived')
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { Router, RouterModule } from '@angular/router'
import { IXpertWorkspace } from '@metad/contracts'
Expand All @@ -22,7 +22,9 @@ export class XpertWorkspaceWelcomeComponent {
readonly #router = inject(Router)
readonly me = injectUser()

readonly workspaces = this.homeComponent.workspaces
readonly workspaces = computed(() => {
return this.homeComponent.workspaces()?.slice(0, 10)
})

newWorkspace() {
this.homeComponent.newWorkspace()
Expand Down
Loading

0 comments on commit 9d64939

Please sign in to comment.