+
@@ -81,16 +70,23 @@
}
+ }
-
- @if (retrievalAgents.length > 0 && maxRetrievalAgents !== -1) {
-
- {{ 'account.kb.count' | translate: { count: retrievalAgents.length, max: maxRetrievalAgents } }}
-
- }
-
+ @if (canAddArag | async) {
+ @if (retrievalAgents.length > 0 && maxRetrievalAgents !== -1) {
+
+ {{ 'account.kb.count' | translate: { count: retrievalAgents.length, max: maxRetrievalAgents } }}
+
+ }
+
+ = maxRetrievalAgents && maxRetrievalAgents !== -1"
+ (click)="createArag()">
+ {{ 'account.retrieval-agents.create' | translate }}
+
}
diff --git a/libs/common/src/lib/account/account-arag/arag-list/arag-list.component.scss b/libs/common/src/lib/account/account-arag/arag-list/arag-list.component.scss
index 6d989022a6..0f805528cf 100644
--- a/libs/common/src/lib/account/account-arag/arag-list/arag-list.component.scss
+++ b/libs/common/src/lib/account/account-arag/arag-list/arag-list.component.scss
@@ -1,13 +1,32 @@
@use 'apps/dashboard/src/variables' as *;
+@use '../../account';
.account-arag {
+ min-height: auto;
+ padding: 0;
+
+ .page-subtitle {
+ display: flex;
+ align-items: center;
+ gap: rhythm(1);
+ }
+
.account-arag-header {
display: flex;
- justify-content: space-between;
- gap: rhythm(4);
+ flex-direction: column;
margin-bottom: rhythm(4);
}
+ .account-arag-counter {
+ font-weight: var(--stf-font-weight-semibold);
+ margin-top: rhythm(4);
+ margin-bottom: rhythm(2);
+ }
+
+ .create-arag-button {
+ margin-top: rhythm(3);
+ }
+
.go-to-arag {
@include body-s();
cursor: pointer;
diff --git a/libs/common/src/lib/account/account-billing/account-billing.component.html b/libs/common/src/lib/account/account-billing/account-billing.component.html
new file mode 100644
index 0000000000..ac6bccccd3
--- /dev/null
+++ b/libs/common/src/lib/account/account-billing/account-billing.component.html
@@ -0,0 +1,51 @@
+
+ @if (isTrial | async) {
+
+ }
+ @if (isSubscribed | async) {
+
+
+ {{ 'billing.usage.label' | translate }}
+
+
+ {{ 'billing.payment_details' | translate }}
+
+
+ {{ 'billing.my-subscription' | translate }}
+
+
+ {{ 'billing.history.link' | translate }}
+
+
+ }
+
diff --git a/libs/common/src/lib/account/account-billing/account-billing.component.scss b/libs/common/src/lib/account/account-billing/account-billing.component.scss
new file mode 100644
index 0000000000..b2790002a5
--- /dev/null
+++ b/libs/common/src/lib/account/account-billing/account-billing.component.scss
@@ -0,0 +1,12 @@
+@use 'apps/dashboard/src/variables' as *;
+
+.trial-status-top {
+ background: $color-light-stronger;
+ border: 1px solid $color-neutral-lighter;
+ border-radius: rhythm(1);
+ display: inline-block;
+ max-width: 100%;
+ margin-bottom: rhythm(4);
+ padding: rhythm(2) rhythm(2.5) rhythm(1.5);
+ width: fit-content;
+}
diff --git a/libs/common/src/lib/account/account-billing/account-billing.component.ts b/libs/common/src/lib/account/account-billing/account-billing.component.ts
new file mode 100644
index 0000000000..c8399d2f60
--- /dev/null
+++ b/libs/common/src/lib/account/account-billing/account-billing.component.ts
@@ -0,0 +1,35 @@
+import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit } from '@angular/core';
+import { BackendConfigurationService, BillingService, FeaturesService, injectScript } from '@flaps/core';
+import { WINDOW } from '@ng-web-apis/common';
+import { AccountPageBase } from '../account-page-base';
+
+@Component({
+ selector: 'app-account-billing',
+ templateUrl: './account-billing.component.html',
+ styleUrl: './account-billing.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: false,
+})
+export class AccountBillingComponent extends AccountPageBase implements OnInit, OnDestroy {
+ private readonly billing = inject(BillingService);
+ private readonly features = inject(FeaturesService);
+ private readonly window = inject(WINDOW);
+ private readonly backendConfig = inject(BackendConfigurationService);
+
+ isSubscribed = this.billing.isSubscribedToStripe;
+ isTrial = this.features.isTrial;
+ noStripe = this.backendConfig.noStripe();
+
+ ngOnInit(): void {
+ const hubSpotApi = (this.window as any)?.HubSpotConversations;
+ if (hubSpotApi) {
+ hubSpotApi.widget?.load();
+ } else {
+ injectScript('//js-eu1.hs-scripts.com/139773752.js').subscribe();
+ }
+ }
+
+ ngOnDestroy(): void {
+ (this.window as any)?.HubSpotConversations?.widget?.remove();
+ }
+}
diff --git a/libs/common/src/lib/account/account-configuration/account-configuration.component.html b/libs/common/src/lib/account/account-configuration/account-configuration.component.html
new file mode 100644
index 0000000000..5a326b58ab
--- /dev/null
+++ b/libs/common/src/lib/account/account-configuration/account-configuration.component.html
@@ -0,0 +1,28 @@
+
+
+
+ {{ 'navbar.nua-keys' | translate }}
+
+ @if (isModelManagementEnabled | async) {
+
+ {{ 'navbar.models' | translate }}
+
+ }
+
+
diff --git a/libs/common/src/lib/account/account-configuration/account-configuration.component.scss b/libs/common/src/lib/account/account-configuration/account-configuration.component.scss
new file mode 100644
index 0000000000..73e6cd373f
--- /dev/null
+++ b/libs/common/src/lib/account/account-configuration/account-configuration.component.scss
@@ -0,0 +1 @@
+@use 'apps/dashboard/src/variables' as *;
diff --git a/libs/common/src/lib/account/account-configuration/account-configuration.component.ts b/libs/common/src/lib/account/account-configuration/account-configuration.component.ts
new file mode 100644
index 0000000000..65c5c53145
--- /dev/null
+++ b/libs/common/src/lib/account/account-configuration/account-configuration.component.ts
@@ -0,0 +1,15 @@
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { FeaturesService } from '@flaps/core';
+import { AccountPageBase } from '../account-page-base';
+
+@Component({
+ selector: 'app-account-configuration',
+ templateUrl: './account-configuration.component.html',
+ styleUrl: './account-configuration.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: false,
+})
+export class AccountConfigurationComponent extends AccountPageBase {
+ private features = inject(FeaturesService);
+ isModelManagementEnabled = this.features.unstable.modelManagement;
+}
diff --git a/libs/common/src/lib/account/account-home/account-consumption.component.html b/libs/common/src/lib/account/account-home/account-consumption.component.html
new file mode 100644
index 0000000000..964668df00
--- /dev/null
+++ b/libs/common/src/lib/account/account-home/account-consumption.component.html
@@ -0,0 +1,59 @@
+
{{ 'account.consumption' | translate }}
+
{{ 'account.consumption-description' | translate }}
+
+
+
+
+
+
{{ 'account.knowledgeboxes' | translate }}
+ @if (kbs | async; as kbList) {
+ @if (kbList.length > 0) {
+
+ @for (kb of kbList; track kb.id) {
+ -
+
+
{{ kb.title }}
+ @if (kb.state === 'PRIVATE') {
+
+ }
+
+ @if (tokensCount?.[kb.id]; as tokens) {
+ @if (tokens > 0) {
+ ({{ tokens | number: '1.0-0' }} tokens)
+ }
+ }
+
+ }
+
+ }
+ }
+
+
+
+ @if (totalQueries | async; as count) {
+
{{ 'account.total_queries' | translate }}
+
+
+
{{ 'metrics.period.last-30-days' | translate }}
+
{{ count.month | number }}
+
+
+
{{ 'metrics.period.last-12-months' | translate }}
+
{{ count.year | number }}
+
+
+
{{ 'metrics.period.since-creation' | translate }}
+
{{ count.sinceCreation | number }}
+
+
+ }
+
+
diff --git a/libs/common/src/lib/account/account-home/account-consumption.component.scss b/libs/common/src/lib/account/account-home/account-consumption.component.scss
new file mode 100644
index 0000000000..84480475a4
--- /dev/null
+++ b/libs/common/src/lib/account/account-home/account-consumption.component.scss
@@ -0,0 +1,87 @@
+@use 'apps/dashboard/src/variables' as *;
+@use '../account';
+
+.consumption-summary {
+ display: flex;
+ flex-direction: column;
+ gap: rhythm(4);
+ margin-top: rhythm(4);
+}
+
+.summary-section {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+
+ h3 {
+ margin: 0;
+ }
+
+ .title-s {
+ margin: 0;
+ }
+}
+
+.kb-summary-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: rhythm(1.5);
+ max-width: 300px;
+}
+
+.kb-summary-item {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ padding: rhythm(1.5) 0;
+ background: $color-light-stronger;
+ border-radius: rhythm(0.5);
+ color: inherit;
+ cursor: pointer;
+ gap: rhythm(1.5);
+
+ &.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+
+ .kb-info {
+ display: flex;
+ align-items: center;
+ gap: rhythm(1);
+
+ .kb-title {
+ word-break: break-word;
+ }
+
+ .lock-icon {
+ flex-shrink: 0;
+ }
+ }
+
+ .kb-tokens {
+ color: $color-neutral-regular;
+ font-size: 0.875rem;
+ flex-shrink: 0;
+ }
+}
+
+.queries-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: rhythm(2);
+ margin-top: rhythm(1);
+ max-width: 500px;
+}
+
+.query-stat {
+ display: flex;
+ flex-direction: column;
+ gap: rhythm(0.5);
+ padding: rhythm(2) 0;
+ background: $color-light-stronger;
+ border-radius: rhythm(0.5);
+}
diff --git a/libs/common/src/lib/account/account-home/account-consumption.component.ts b/libs/common/src/lib/account/account-home/account-consumption.component.ts
new file mode 100644
index 0000000000..432b13fa53
--- /dev/null
+++ b/libs/common/src/lib/account/account-home/account-consumption.component.ts
@@ -0,0 +1,84 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnDestroy, OnInit } from '@angular/core';
+import { SDKService } from '@flaps/core';
+import { UsagePoint, UsageType } from '@nuclia/core';
+import { combineLatest, forkJoin, map, ReplaySubject, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
+import { MetricsService } from '../metrics.service';
+
+@Component({
+ selector: 'app-account-consumption',
+ templateUrl: './account-consumption.component.html',
+ styleUrl: './account-consumption.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: false,
+})
+export class AccountConsumptionComponent implements OnInit, OnDestroy {
+ private metrics = inject(MetricsService);
+ private sdk = inject(SDKService);
+ private cdr = inject(ChangeDetectorRef);
+
+ private unsubscribeAll = new Subject
();
+
+ selectedPeriod = new ReplaySubject<{ start: Date; end: Date }>(1);
+
+ usage?: { [key: string]: UsagePoint[] };
+ tokensCount?: { [key: string]: number };
+
+ kbs = this.sdk.kbList;
+ totalQueries = this.metrics.getUsageCount(UsageType.SEARCHES_PERFORMED);
+
+ ngOnInit() {
+ this.metrics.period.pipe(takeUntil(this.unsubscribeAll)).subscribe((period) => {
+ this.selectedPeriod.next(period);
+ });
+
+ this.getUsageMap()
+ .pipe(takeUntil(this.unsubscribeAll))
+ .subscribe((usage) => {
+ this.usage = usage;
+ this.tokensCount = Object.entries(usage).reduce(
+ (acc, [key, value]) => {
+ if (key !== 'account') {
+ acc[key] = value[0]?.metrics?.find((m) => m.name === 'nuclia_tokens_billed')?.value || 0;
+ }
+ return acc;
+ },
+ {} as { [key: string]: number },
+ );
+ this.cdr.markForCheck();
+ });
+ }
+
+ ngOnDestroy() {
+ this.unsubscribeAll.next();
+ this.unsubscribeAll.complete();
+ }
+
+ private getUsageMap() {
+ return combineLatest([this.metrics.account$, this.selectedPeriod, this.kbs]).pipe(
+ switchMap(([account, period, kbs]) => {
+ const requests = kbs
+ .map((kb) =>
+ this.sdk.nuclia.db
+ .getUsage(account.id, period.start.toISOString(), period.end.toISOString(), kb.id)
+ .pipe(map((usage) => ({ key: kb.id, usage }))),
+ )
+ .concat([
+ this.sdk.nuclia.db
+ .getUsage(account.id, period.start.toISOString(), period.end.toISOString())
+ .pipe(map((usage) => ({ key: 'account', usage }))),
+ ]);
+ return forkJoin(requests);
+ }),
+ map((usage) =>
+ usage.reduce(
+ (acc, curr) => {
+ acc[curr.key] = curr.usage;
+ return acc;
+ },
+ {} as { [key: string]: UsagePoint[] },
+ ),
+ ),
+ shareReplay(1),
+ );
+ }
+}
diff --git a/libs/common/src/lib/account/account-home/account-settings.component.html b/libs/common/src/lib/account/account-home/account-settings.component.html
new file mode 100644
index 0000000000..dc657706f7
--- /dev/null
+++ b/libs/common/src/lib/account/account-home/account-settings.component.html
@@ -0,0 +1,36 @@
+
+
+
+ {{ 'account.consumption' | translate }}
+
+ @if (showAccountSettingsTab | async) {
+
+ {{ 'account.manage' | translate }}
+
+ }
+
+ {{ 'generic.user_preferences' | translate }}
+
+
+
diff --git a/libs/common/src/lib/account/account-home/account-settings.component.scss b/libs/common/src/lib/account/account-home/account-settings.component.scss
new file mode 100644
index 0000000000..73e6cd373f
--- /dev/null
+++ b/libs/common/src/lib/account/account-home/account-settings.component.scss
@@ -0,0 +1 @@
+@use 'apps/dashboard/src/variables' as *;
diff --git a/libs/common/src/lib/account/account-home/account-settings.component.ts b/libs/common/src/lib/account/account-home/account-settings.component.ts
new file mode 100644
index 0000000000..b82243c7bc
--- /dev/null
+++ b/libs/common/src/lib/account/account-home/account-settings.component.ts
@@ -0,0 +1,25 @@
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { FeaturesService } from '@flaps/core';
+import { combineLatest, map, shareReplay } from 'rxjs';
+import { AccountPageBase } from '../account-page-base';
+
+@Component({
+ selector: 'app-account-settings',
+ templateUrl: './account-settings.component.html',
+ styleUrl: './account-settings.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: false,
+})
+export class AccountSettingsComponent extends AccountPageBase {
+ private features = inject(FeaturesService);
+
+ isCowork = this.sdk.currentAccount.pipe(
+ map((account) => account.workflow === 'cowork'),
+ shareReplay(1),
+ );
+
+ showAccountSettingsTab = combineLatest([this.features.isAccountManager, this.isCowork]).pipe(
+ map(([isManager, isCowork]) => !!isManager && !isCowork),
+ shareReplay(1),
+ );
+}
diff --git a/libs/common/src/lib/account/account-home/simple-account-home.component.html b/libs/common/src/lib/account/account-home/simple-account-home.component.html
index 4018535e1d..dfbf90d3b8 100644
--- a/libs/common/src/lib/account/account-home/simple-account-home.component.html
+++ b/libs/common/src/lib/account/account-home/simple-account-home.component.html
@@ -6,7 +6,7 @@
}
diff --git a/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.html b/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.html
index 3c8f0e9c1d..8505655dae 100644
--- a/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.html
+++ b/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.html
@@ -5,8 +5,8 @@
}
@if (account && knowledgeBoxes) {
-
-
{{ 'account.related_kbs' | translate }}
+
+
{{ 'account.related_kbs' | translate }}
{{ 'account.kb-description' | translate }}
@@ -67,24 +67,21 @@
{{ 'account.related_kbs' | translate }}
}
@if (canAddKb | async) {
-
- @if (knowledgeBoxes.length > 0 && maxKnowledgeBoxes !== -1) {
-
- {{ 'account.kb.count' | translate: { count: knowledgeBoxes.length, max: maxKnowledgeBoxes } }}
-
- }
+ @if (knowledgeBoxes.length > 0 && maxKnowledgeBoxes !== -1) {
+
+ {{ 'account.kb.count' | translate: { count: knowledgeBoxes.length, max: maxKnowledgeBoxes } }}
+
+ }
- = maxKnowledgeBoxes && maxKnowledgeBoxes !== -1"
- routerLink="./create">
- {{ 'account.kb.create' | translate }}
-
-
+
= maxKnowledgeBoxes && maxKnowledgeBoxes !== -1"
+ [routerLink]="['/at', account.slug, 'manage', 'administration', 'knowledge-boxes', 'create']"
+ class="add-kb-button">
+ {{ 'account.kb.create' | translate }}
+
}
}
diff --git a/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.scss b/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.scss
index ca582a61df..e67d173aed 100644
--- a/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.scss
+++ b/libs/common/src/lib/account/account-kbs/kb-list/kb-list.component.scss
@@ -1,13 +1,17 @@
@use 'apps/dashboard/src/variables' as *;
+@use '../../account';
.account-kbs {
+ min-height: auto;
+ padding: 0;
+
h2 {
margin: 0 0 rhythm(1) 0;
}
.account-kbs-list {
margin-top: rhythm(5);
- max-width: $width-large-screen;
+ max-width: 640px;
}
.account-kb {
@@ -37,17 +41,16 @@
&-counter {
font-weight: var(--stf-font-weight-semibold);
+ margin-top: rhythm(4);
+ margin-bottom: rhythm(2);
}
- &-empty {
- font-style: italic;
+ .add-kb-button {
+ margin-top: rhythm(3);
}
- .add-kb-container {
- align-items: center;
- display: flex;
- gap: rhythm(3);
- margin-top: rhythm(3);
+ &-empty {
+ font-style: italic;
}
}
diff --git a/libs/common/src/lib/account/account-manage/account-manage.component.html b/libs/common/src/lib/account/account-manage/account-manage.component.html
index 31b5db1341..f6381f511e 100644
--- a/libs/common/src/lib/account/account-manage/account-manage.component.html
+++ b/libs/common/src/lib/account/account-manage/account-manage.component.html
@@ -1,148 +1,146 @@
-
-
-
-
{{ 'account.manage' | translate }}
-
-
-
-
{{ 'account.saml.title' | translate }}
-