diff --git a/package-lock.json b/package-lock.json index 670417d..bc5f529 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,17 @@ "version": "0.0.0", "dependencies": { "@angular/animations": "^21.0.4", - "@angular/cdk": "^21.0.3", + "@angular/cdk": "^21.0.5", "@angular/common": "^21.0.4", "@angular/compiler": "^21.0.4", "@angular/core": "^21.0.4", "@angular/elements": "^21.0.8", "@angular/forms": "^21.0.4", + "@angular/material": "^21.0.5", "@angular/platform-browser": "^21.0.4", "@angular/platform-browser-dynamic": "^21.0.4", "@angular/router": "^21.0.0", + "@fontsource/material-icons-outlined": "^5.2.6", "bootstrap": "^5.3.8", "bootstrap-icons": "^1.13.1", "class-variance-authority": "^0.7.1", @@ -467,9 +469,9 @@ } }, "node_modules/@angular/cdk": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.0.3.tgz", - "integrity": "sha512-abfckeZfFvovdpxuQHRE4gS1VLNa05Dx0ZSKLGVL9DsQsi4pgn6wWg1y9TkXMlmtpG/EhLmCBxUc6LOHfdeWQA==", + "version": "21.0.5", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.0.5.tgz", + "integrity": "sha512-yO/IRYEZ5wJkpwg3GT3b6RST4pqNFTAhuyPdEdLcE81cs283K3aKOsCYh2xUR3bR4WxBh2kBPSJ31AFZyJXbSA==", "license": "MIT", "dependencies": { "parse5": "^8.0.0", @@ -638,31 +640,21 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@angular/localize": { - "version": "21.0.6", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-21.0.6.tgz", - "integrity": "sha512-t4Jf4px49YAuF7G0eLxknB+KNE+tdqzhAo2w//9kjZGg/xHPp9H/RQUlNUB/RYJuabbBh4IpiRvpJZqp7rKt8A==", - "dev": true, + "node_modules/@angular/material": { + "version": "21.0.5", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-21.0.5.tgz", + "integrity": "sha512-LcDotwwTl2GFtFK1BqcKjj0eRAkOjTSj1C6ux0rOlpU2hJaECxue/ZqEyB/KMHngFAqMJpecxF4zGy/ISCp2xA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "7.28.4", - "@types/babel__core": "7.20.5", - "tinyglobby": "^0.2.12", - "yargs": "^18.0.0" - }, - "bin": { - "localize-extract": "tools/bundles/src/extract/cli.js", - "localize-migrate": "tools/bundles/src/migrate/cli.js", - "localize-translate": "tools/bundles/src/translate/cli.js" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/compiler": "21.0.6", - "@angular/compiler-cli": "21.0.6" + "@angular/cdk": "21.0.5", + "@angular/common": "^21.0.0 || ^22.0.0", + "@angular/core": "^21.0.0 || ^22.0.0", + "@angular/forms": "^21.0.0 || ^22.0.0", + "@angular/platform-browser": "^21.0.0 || ^22.0.0", + "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { @@ -1694,6 +1686,15 @@ "node": ">=18" } }, + "node_modules/@fontsource/material-icons-outlined": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/material-icons-outlined/-/material-icons-outlined-5.2.6.tgz", + "integrity": "sha512-99XKAkwnCg0s0/ywax+o3m01HSNM5gGzSBw+WnlWG2+WY3wOjcN+wXMfm4zP37Yme7Yze2DvKxF78tHWOrlwFw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@inquirer/ansi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", @@ -3356,17 +3357,6 @@ "license": "MIT", "optional": true }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-beta.47", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.47.tgz", @@ -4021,6 +4011,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, "license": "MIT" }, "node_modules/@tailwindcss/node": { @@ -4355,59 +4346,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.28.2" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -10253,13 +10191,6 @@ "peerDependencies": { "zod": "^3.25 || ^4" } - }, - "node_modules/zone.js": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.16.0.tgz", - "integrity": "sha512-LqLPpIQANebrlxY6jKcYKdgN5DTXyyHAKnnWWjE5pPfEQ4n7j5zn7mOEEpwNZVKGqx3kKKmvplEmoBrvpgROTA==", - "license": "MIT", - "peer": true } } } diff --git a/package.json b/package.json index e7ee28d..ef38a0b 100644 --- a/package.json +++ b/package.json @@ -24,15 +24,17 @@ "packageManager": "npm@10.8.2", "dependencies": { "@angular/animations": "^21.0.4", - "@angular/cdk": "^21.0.3", + "@angular/cdk": "^21.0.5", "@angular/common": "^21.0.4", "@angular/compiler": "^21.0.4", "@angular/core": "^21.0.4", "@angular/elements": "^21.0.8", "@angular/forms": "^21.0.4", + "@angular/material": "^21.0.5", "@angular/platform-browser": "^21.0.4", "@angular/platform-browser-dynamic": "^21.0.4", "@angular/router": "^21.0.0", + "@fontsource/material-icons-outlined": "^5.2.6", "bootstrap": "^5.3.8", "bootstrap-icons": "^1.13.1", "class-variance-authority": "^0.7.1", diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 7498b00..6680ab8 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,6 +1,7 @@ import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { provideAnimations } from '@angular/platform-browser/animations'; import { authTokenInterceptor } from './interceptors/auth-token.interceptor'; import { routes } from './app.routes'; @@ -8,6 +9,7 @@ import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), + provideAnimations(), provideHttpClient(withInterceptors([authTokenInterceptor])), provideRouter(routes) ] diff --git a/src/app/layouts/app-layout/app-layout.css b/src/app/layouts/app-layout/app-layout.css index 9cd6f0d..d77ead2 100644 --- a/src/app/layouts/app-layout/app-layout.css +++ b/src/app/layouts/app-layout/app-layout.css @@ -3,6 +3,12 @@ font-family: 'Roboto', sans-serif; } +.app-topbar { + height: 56px !important; + min-height: 56px !important; + padding: 0 16px !important; +} + /* Logo */ .logo { height: 40px; @@ -18,15 +24,44 @@ min-height: 0; /* necessario per overflow-auto su flex */ } -/* Tabs sticky */ -.nav-tabs { - border-bottom: 1px solid #ccc; +.app-tabbar-wrap { + position: fixed; + top: 56px; + z-index: 40; + width: 100%; + padding: 0 16px; + background: #d8f4e7; + border-bottom: 1px solid rgba(148, 163, 184, 0.28); } -.nav-link.active { - background-color: #ffffff; - color: #0f766e; /* verde elegante */ - border: 1px solid #d1d5db; - border-bottom-color: white; - box-shadow: 0 -1px 6px rgba(0, 0, 0, 0.05); +.app-tabbar { + min-height: 48px; + padding: 0; + background: transparent; +} + +.app-tab-link { + min-width: 120px; + min-height: 48px; + border-radius: 0; + font-weight: 600; +} + +.app-shell { + display: flex; + flex: 1; + flex-direction: column; + height: 100vh; + overflow: hidden; + padding-top: 112px; +} + +.app-main-content { + width: 100%; + flex: 1 1 auto; + min-height: 0; + margin-top: 0; + padding: 10px 16px 16px; + overflow: hidden; + background: #ffffff; } diff --git a/src/app/layouts/app-layout/app-layout.html b/src/app/layouts/app-layout/app-layout.html index 126167b..ebb7e37 100644 --- a/src/app/layouts/app-layout/app-layout.html +++ b/src/app/layouts/app-layout/app-layout.html @@ -1,72 +1,50 @@ -
- - -
-
- -

HumAIn Flow

-
- -
- - {{ loggedUser()?.username }} -
- - - - -
- Logout -
-
- -
-
- - -
- - - - - -
- -
- +
+ +
+ Logo +

HumAIn Flow

-
\ No newline at end of file + + + +
+ + {{ loggedUser()?.username }} + +
+ + +
+ + +
+ +
+
+
diff --git a/src/app/layouts/app-layout/app-layout.ts b/src/app/layouts/app-layout/app-layout.ts index 215b672..9b90784 100644 --- a/src/app/layouts/app-layout/app-layout.ts +++ b/src/app/layouts/app-layout/app-layout.ts @@ -1,10 +1,15 @@ -import { Component, inject } from '@angular/core'; +import { afterNextRender, Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatToolbarModule } from '@angular/material/toolbar'; import { Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { Authorization } from '@services/authorization/authorization'; +import { BlocksService } from '@services/blocks/blocks'; @Component({ selector: 'app-app-layout', - imports: [RouterOutlet, RouterLink, RouterLinkActive], + imports: [RouterOutlet, RouterLink, RouterLinkActive, MatButtonModule, MatIconModule, MatTabsModule, MatToolbarModule], templateUrl: './app-layout.html', styleUrl: './app-layout.css', }) @@ -13,8 +18,17 @@ export class AppLayout { router = inject(Router); private authService = inject(Authorization); + private blocksService = inject(BlocksService); loggedUser = this.authService.loggedInUser; + + constructor() { + afterNextRender(() => { + void this.blocksService.getAllBlocksTypes().catch((err) => { + console.error('Blocks preload failed', err); + }); + }); + } logout() { this.authService.logout(); diff --git a/src/app/layouts/flow-editor/flow-editor.html b/src/app/layouts/flow-editor/flow-editor.html index 766dba2..9d5726f 100644 --- a/src/app/layouts/flow-editor/flow-editor.html +++ b/src/app/layouts/flow-editor/flow-editor.html @@ -12,12 +12,12 @@
@if (flow()) { -
+
-
+ } @else {

No flow selected

@@ -31,9 +31,10 @@ @@ -41,9 +42,9 @@ } @else {
- +
AI
- +
} diff --git a/src/app/layouts/flow-editor/flow-editor.ts b/src/app/layouts/flow-editor/flow-editor.ts index 7f14fae..da32d0c 100644 --- a/src/app/layouts/flow-editor/flow-editor.ts +++ b/src/app/layouts/flow-editor/flow-editor.ts @@ -1,4 +1,7 @@ import { Component, inject, signal } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatIconModule } from '@angular/material/icon'; import { environment } from '@environment'; import { EditorSidebar } from "@pages/main/editor-sidebar/editor-sidebar"; import { EditorStateHolder } from '@stores/flow-editor'; @@ -8,7 +11,7 @@ import { ReteEditor } from "@shared/rete-editor/rete-editor"; @Component({ selector: 'app-flow-editor', - imports: [EditorSidebar, TitleToolbar, ReteEditor, FlowAssistant], + imports: [EditorSidebar, TitleToolbar, ReteEditor, FlowAssistant, MatButtonModule, MatCardModule, MatIconModule], templateUrl: './flow-editor.html', styleUrl: './flow-editor.css', }) diff --git a/src/app/layouts/tasks-executor/tasks-executor.html b/src/app/layouts/tasks-executor/tasks-executor.html index 1dcdfca..7a42142 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.html +++ b/src/app/layouts/tasks-executor/tasks-executor.html @@ -1,15 +1,15 @@
-
+ -
+
diff --git a/src/app/layouts/tasks-executor/tasks-executor.ts b/src/app/layouts/tasks-executor/tasks-executor.ts index 5b14f93..f4e7e83 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.ts +++ b/src/app/layouts/tasks-executor/tasks-executor.ts @@ -1,4 +1,5 @@ import { Component, computed, effect, inject, signal } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; import { normalizeExecutionStatus, TaskExecution } from '@models/task-execution'; import { TaskExecutionListItem, @@ -10,7 +11,7 @@ import { TaskExecutionsService } from '@services/task-executions/task-executions @Component({ selector: 'app-tasks-executor', - imports: [TasksExecutionsListComponent, TaskExecutionViewerComponent], + imports: [TasksExecutionsListComponent, TaskExecutionViewerComponent, MatCardModule], templateUrl: './tasks-executor.html', styleUrl: './tasks-executor.css', }) diff --git a/src/app/pages/auth/login/login.css b/src/app/pages/auth/login/login.css index 4af578e..6589d14 100644 --- a/src/app/pages/auth/login/login.css +++ b/src/app/pages/auth/login/login.css @@ -3,3 +3,99 @@ transform: translateY(20px); transition: opacity 500ms ease-out, transform 500ms ease-out; } + +.auth-page { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 16px; +} + +.auth-card { + width: 100%; + max-width: 32rem; + margin: 0 auto; + padding: 28px; + border: 1px solid #bae6fd; + border-radius: 24px; + box-shadow: 0 20px 40px rgba(15, 23, 42, 0.14); +} + +.auth-logo-wrap { + display: flex; + justify-content: center; +} + +.auth-logo { + width: 12rem; + height: 12rem; + object-fit: contain; +} + +.auth-card-body { + padding: 16px; +} + +.auth-field, +.auth-submit { + width: 100%; +} + +.auth-links { + margin-bottom: 12px; + text-align: center; +} + +.auth-link { + margin-left: 4px; + color: #2563eb; + text-decoration: none; +} + +.auth-link:hover { + text-decoration: underline; +} + +.auth-banner { + position: fixed; + top: 16px; + left: 50%; + transform: translateX(-50%); + z-index: 50; + display: flex; + align-items: flex-start; + gap: 12px; + width: 100%; + max-width: 32rem; + padding: 12px 16px; + border-radius: 10px; + border: 1px solid; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.14); +} + +.auth-banner-error { + border-color: #fca5a5; + background: #fee2e2; + color: #991b1b; +} + +.auth-banner-success { + border-color: #86efac; + background: #dcfce7; + color: #166534; +} + +.auth-banner-icon { + margin-top: 2px; +} + +.auth-banner-body { + flex: 1; + font-size: 14px; + line-height: 1.5; +} + +.auth-banner-body p { + margin: 0; +} diff --git a/src/app/pages/auth/login/login.html b/src/app/pages/auth/login/login.html index cf2a60c..9288fa8 100644 --- a/src/app/pages/auth/login/login.html +++ b/src/app/pages/auth/login/login.html @@ -1,83 +1,57 @@ @if (error()) { -
- - - - - -
-

- {{ error() }} -

+
+ +
+

{{ error() }}

} @if (registeredUser()) { -
- - - -
-

+

+ +
+

Registration successful. {{ registeredUser() }}, now you can log in.

} -
-
-
- +
+ +
+
-
+
- -
- - -
- -
- - - -
+ - -
+ - -
-
+
diff --git a/src/app/pages/auth/login/login.ts b/src/app/pages/auth/login/login.ts index 4fffd54..2696f5b 100644 --- a/src/app/pages/auth/login/login.ts +++ b/src/app/pages/auth/login/login.ts @@ -1,5 +1,10 @@ import { Component, effect, inject, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; import { Router, RouterLink } from "@angular/router"; import { FormUtility } from '@utilities/form-utility'; import { Authorization } from '@services/authorization/authorization'; @@ -8,7 +13,7 @@ import { Field, form, required } from '@angular/forms/signals'; @Component({ selector: 'app-login', - imports: [FormsModule, RouterLink, Field], + imports: [FormsModule, RouterLink, Field, MatButtonModule, MatCardModule, MatFormFieldModule, MatIconModule, MatInputModule], templateUrl: './login.html', styleUrls: ['./login.css'], }) diff --git a/src/app/pages/auth/signup/signup.css b/src/app/pages/auth/signup/signup.css index e69de29..28c6ebd 100644 --- a/src/app/pages/auth/signup/signup.css +++ b/src/app/pages/auth/signup/signup.css @@ -0,0 +1,78 @@ +.signup-page { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 16px; +} + +.signup-card { + width: 100%; + max-width: 48rem; + padding: 28px; + border: 1px solid #bae6fd; + border-radius: 24px; + box-shadow: 0 20px 40px rgba(15, 23, 42, 0.14); +} + +.signup-logo-wrap { + display: flex; + justify-content: center; +} + +.signup-logo { + width: 12rem; + height: 12rem; + object-fit: contain; +} + +.signup-card-body { + padding: 16px; +} + +.signup-form { + display: flex; + flex-direction: column; + gap: 12px; +} + +.signup-row { + display: flex; + flex-direction: column; + gap: 12px; +} + +.signup-field, +.signup-submit { + width: 100%; +} + +.signup-links { + margin-bottom: 4px; + text-align: center; +} + +.signup-link { + margin-left: 4px; + color: #2563eb; + text-decoration: none; +} + +.signup-link:hover { + text-decoration: underline; +} + +.signup-error { + padding: 16px; + border: 1px solid #fecaca; + border-radius: 10px; + background: #fef2f2; + color: #991b1b; + font-size: 14px; +} + +@media (min-width: 1024px) { + .signup-row { + flex-direction: row; + } +} diff --git a/src/app/pages/auth/signup/signup.html b/src/app/pages/auth/signup/signup.html index 7dfd2b7..de55713 100644 --- a/src/app/pages/auth/signup/signup.html +++ b/src/app/pages/auth/signup/signup.html @@ -1,113 +1,88 @@ -
- -
-
- -
-
- -
-
- - - - @if (isInvalid(signupForm.fullname())) { - {{ signupForm.fullname().errors()[0].message }} - } -
-
-
- - - - @if (isInvalid(signupForm.email())) { - {{ signupForm.email().errors()[0].message }} - } -
-
- - - @if (isInvalid(signupForm.username())) { - {{ signupForm.username().errors()[0].message }} - } -
-
-
-
- -
- - - @if (isInvalid(signupForm.password())) { - {{ signupForm.password().errors()[0].message }} - } -
- -
-
- -
- - - @if (!isInvalid(signupForm.password()) && isInvalid(signupForm.confirmPassword())) { - {{ - signupForm.confirmPassword().errors()[0].message}} - } -
- -
-
-
- - Already have an account? - Sign In - -
- @if(error()) { - - } @else { - @if (!isRegistering()){ - - - } @else { - - } - } -
-
+ diff --git a/src/app/pages/auth/signup/signup.ts b/src/app/pages/auth/signup/signup.ts index 067df8d..c9fbc9e 100644 --- a/src/app/pages/auth/signup/signup.ts +++ b/src/app/pages/auth/signup/signup.ts @@ -1,5 +1,9 @@ import { Component, effect, inject, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { form, Field, minLength, email, required, validate, maxLength, disabled } from '@angular/forms/signals' import { Router, RouterLink } from '@angular/router'; import { UserRegistration } from '@models/user'; @@ -9,7 +13,7 @@ import { FormUtility } from '@utilities/form-utility'; @Component({ selector: 'app-signup', - imports: [FormsModule, Field, RouterLink], + imports: [FormsModule, Field, RouterLink, MatButtonModule, MatCardModule, MatFormFieldModule, MatInputModule], templateUrl: './signup.html', styleUrl: './signup.css', }) diff --git a/src/app/pages/main/editor-sidebar/editor-sidebar.css b/src/app/pages/main/editor-sidebar/editor-sidebar.css index c9fda45..edb4e2d 100644 --- a/src/app/pages/main/editor-sidebar/editor-sidebar.css +++ b/src/app/pages/main/editor-sidebar/editor-sidebar.css @@ -1,3 +1,14 @@ +.editor-sidebar-shell { + height: 100%; + min-height: 0; +} + +.editor-sidebar-content { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + .enabled:hover { background-color: var(--color-gray-100) ; } diff --git a/src/app/pages/main/editor-sidebar/editor-sidebar.html b/src/app/pages/main/editor-sidebar/editor-sidebar.html index e16fafb..1ec1bcc 100644 --- a/src/app/pages/main/editor-sidebar/editor-sidebar.html +++ b/src/app/pages/main/editor-sidebar/editor-sidebar.html @@ -1,10 +1,10 @@
@if (modelPickerOpen()) { - + + Model + + @for (model of models(); track model) { + {{ model }} + } + + } @if (modelsLoading()) { @@ -87,7 +89,7 @@ @if (quickPromptsOpen()) {
@for (starter of starterPrompts; track starter) { - } @@ -130,17 +132,22 @@
- + + Prompt + + - +
-
+ diff --git a/src/app/shared/flows-list/flow-item/flow-item.ts b/src/app/shared/flows-list/flow-item/flow-item.ts index 46878ba..a85219a 100644 --- a/src/app/shared/flows-list/flow-item/flow-item.ts +++ b/src/app/shared/flows-list/flow-item/flow-item.ts @@ -1,5 +1,9 @@ import { CommonModule } from '@angular/common'; import { Component, computed, inject, input, model } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { Flow } from '@models/flow'; import { ConfirmDialogService } from '@services/dialogs/confirm-dialog'; import { FlowsService } from '@services/flows/flows'; @@ -7,7 +11,7 @@ import { EditorStateHolder } from '@stores/flow-editor'; @Component({ selector: 'app-flow-item', - imports: [CommonModule], + imports: [CommonModule, MatButtonModule, MatCardModule, MatIconModule, MatTooltipModule], templateUrl: './flow-item.html', styleUrl: './flow-item.css', }) diff --git a/src/app/shared/flows-list/flows-list.css b/src/app/shared/flows-list/flows-list.css index e69de29..956e857 100644 --- a/src/app/shared/flows-list/flows-list.css +++ b/src/app/shared/flows-list/flows-list.css @@ -0,0 +1,75 @@ +:host { + display: block; + height: 100%; +} + +.flows-list-loading { + display: flex; + align-items: center; + justify-content: center; + height: 8rem; +} + +.flows-list-root { + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; +} + +.flows-list-search { + width: 100%; + padding: 8px 0; +} + +.flows-list-filter { + display: inline-flex; + align-self: center; + margin-bottom: 12px; +} + +.flows-list-ordering { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 4.5rem; + padding: 8px 0; +} + +.flows-list-empty { + display: flex; + flex: 1; + flex-direction: column; + padding-top: 2.5rem; + margin-top: 2.5rem; + color: #94a3b8; + text-align: center; +} + +.flows-list-items { + flex: 1; + width: 100%; + min-height: 0; + max-height: calc(100vh - 380px); + overflow-y: auto; + box-sizing: border-box; + padding: 10px 0 20px !important; + background: transparent; +} + +.flows-list-list-item { + height: auto !important; + padding: 0 !important; + margin-bottom: 10px; + overflow: visible !important; +} + +.flows-list-list-item:last-child { + margin-bottom: 0; +} + +:host ::ng-deep .flows-list-list-item .mdc-list-item__content, +:host ::ng-deep .flows-list-list-item .mat-mdc-list-item-unscoped-content { + overflow: visible !important; +} diff --git a/src/app/shared/flows-list/flows-list.html b/src/app/shared/flows-list/flows-list.html index be5da9a..16294dc 100644 --- a/src/app/shared/flows-list/flows-list.html +++ b/src/app/shared/flows-list/flows-list.html @@ -1,58 +1,38 @@ @if (loading()) { -
-
- Loading... -
+
+
} @else { -
-
-
- - - - - -
+
+ -
+ + All + Public + Private + - - - - - - -
- -
+
@if (filteredFlows().length === 0) { -
+
No flows found
} @else { -
+ @for (flow of filteredFlows(); track flow.id) { - + + + } -
+ }
-} \ No newline at end of file +} diff --git a/src/app/shared/flows-list/flows-list.ts b/src/app/shared/flows-list/flows-list.ts index ceebfcd..af2844a 100644 --- a/src/app/shared/flows-list/flows-list.ts +++ b/src/app/shared/flows-list/flows-list.ts @@ -3,6 +3,13 @@ import { Flow, FlowVisibility } from '@models/flow'; import { FlowsService } from '@services/flows/flows'; import { FlowItem } from './flow-item/flow-item'; import { FormsModule } from '@angular/forms'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatCardModule } from '@angular/material/card'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { OrderEvent, OrderField, Ordering } from "@shared/ordering/ordering"; import { ListStateViewHolder, OrderViewState } from '@utilities/list-state-holder'; @@ -10,7 +17,7 @@ type FlowFilter = FlowVisibility | 'all'; @Component({ selector: 'app-flows-list', - imports: [FlowItem, FormsModule, Ordering], + imports: [FlowItem, FormsModule, Ordering, MatButtonToggleModule, MatCardModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, MatProgressSpinnerModule], templateUrl: './flows-list.html', styleUrl: './flows-list.css', }) diff --git a/src/app/shared/group-holder/group-holder.css b/src/app/shared/group-holder/group-holder.css index e69de29..98ddfba 100644 --- a/src/app/shared/group-holder/group-holder.css +++ b/src/app/shared/group-holder/group-holder.css @@ -0,0 +1,14 @@ +:host { + display: flex; + flex: 1 1 auto; + min-width: 0; + min-height: 0; +} + +.group-holder-body { + display: flex; + flex: 1 1 auto; + flex-direction: column; + min-width: 0; + min-height: 0; +} diff --git a/src/app/shared/group-holder/group-holder.html b/src/app/shared/group-holder/group-holder.html index f0a6b70..6bef807 100644 --- a/src/app/shared/group-holder/group-holder.html +++ b/src/app/shared/group-holder/group-holder.html @@ -22,7 +22,8 @@
+
- +
diff --git a/src/app/shared/human-interaction-dialog/human-interaction-dialog.html b/src/app/shared/human-interaction-dialog/human-interaction-dialog.html index a665298..e36bb0e 100644 --- a/src/app/shared/human-interaction-dialog/human-interaction-dialog.html +++ b/src/app/shared/human-interaction-dialog/human-interaction-dialog.html @@ -8,7 +8,7 @@

{{ currentState.title }}

Confirm the input as node output or edit it before sending.

- +
@@ -23,27 +23,28 @@ @if (editing()) { - + }
- + @if (!editing()) { - - + + } @else { - - + + }
diff --git a/src/app/shared/human-interaction-dialog/human-interaction-dialog.ts b/src/app/shared/human-interaction-dialog/human-interaction-dialog.ts index 8f07cb9..e2ba552 100644 --- a/src/app/shared/human-interaction-dialog/human-interaction-dialog.ts +++ b/src/app/shared/human-interaction-dialog/human-interaction-dialog.ts @@ -1,5 +1,8 @@ import { Component, effect, ElementRef, inject, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { HumanInteractionDialogResult, HumanInteractionDialogService @@ -8,7 +11,7 @@ import { @Component({ selector: 'app-human-interaction-dialog-host', standalone: true, - imports: [FormsModule], + imports: [FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule], templateUrl: './human-interaction-dialog.html' }) export class HumanInteractionDialogHostComponent { diff --git a/src/app/shared/node-settings-dialog/node-settings-dialog.html b/src/app/shared/node-settings-dialog/node-settings-dialog.html index a2795f3..885789e 100644 --- a/src/app/shared/node-settings-dialog/node-settings-dialog.html +++ b/src/app/shared/node-settings-dialog/node-settings-dialog.html @@ -2,19 +2,15 @@
-
+

{{ state()!.title }}

- +
@for (field of fields; track field.key) { -
- - + +
diff --git a/src/app/shared/node-settings-dialog/node-settings-dialog.ts b/src/app/shared/node-settings-dialog/node-settings-dialog.ts index ed62204..da809a0 100644 --- a/src/app/shared/node-settings-dialog/node-settings-dialog.ts +++ b/src/app/shared/node-settings-dialog/node-settings-dialog.ts @@ -1,5 +1,12 @@ import { Component, effect, ElementRef, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { NodeSettingField, NodeSettingsDialogService, @@ -9,7 +16,7 @@ import { @Component({ selector: 'app-node-settings-dialog-host', standalone: true, - imports: [FormsModule], + imports: [FormsModule, MatButtonModule, MatCheckboxModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSelectModule, MatTooltipModule], templateUrl: './node-settings-dialog.html' }) export class NodeSettingsDialogHostComponent { diff --git a/src/app/shared/ordering/ordering.css b/src/app/shared/ordering/ordering.css index fe8bbc5..898070c 100644 --- a/src/app/shared/ordering/ordering.css +++ b/src/app/shared/ordering/ordering.css @@ -2,3 +2,49 @@ display: block; width: 100%; } +:host { + display: block; + width: 100%; +} + +.ordering-root { + width: 100%; +} + +.ordering-field { + width: 100%; +} + +.ordering-suffix { + display: flex; + align-items: center; + gap: 4px; + margin-left: 10px; + margin-right: 6px; + padding-left: 4px; +} + +.ordering-dir-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + border: 0; + border-radius: 8px; + background: transparent; + color: #64748b; + cursor: pointer; + transition: background-color 0.15s ease, color 0.15s ease; +} + +.ordering-dir-btn:hover { + background: #eef2ff; + color: #1d4ed8; +} + +.ordering-dir-btn-active { + background: #dbeafe; + color: #1d4ed8; +} diff --git a/src/app/shared/ordering/ordering.html b/src/app/shared/ordering/ordering.html index 3eabf41..a2e6970 100644 --- a/src/app/shared/ordering/ordering.html +++ b/src/app/shared/ordering/ordering.html @@ -1,42 +1,34 @@ -
-
- - -
- -
- +
+ + Order by + + @for (opt of orderFields(); track opt.field) { + {{ opt.label || opt.field }} + } + - -
+
+ + + +
+
diff --git a/src/app/shared/ordering/ordering.ts b/src/app/shared/ordering/ordering.ts index 0a0d081..900f55e 100644 --- a/src/app/shared/ordering/ordering.ts +++ b/src/app/shared/ordering/ordering.ts @@ -1,5 +1,8 @@ import { Component, effect, Input, input, model, output } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatSelectModule } from '@angular/material/select'; import { OrderViewState } from '@utilities/list-state-holder'; export type OrderEvent = { orderBy: string | null; orderDir: orderDirType }; @@ -9,7 +12,7 @@ export type OrderField = { field: string; label?: string }; @Component({ selector: 'app-ordering', - imports: [FormsModule], + imports: [FormsModule, MatFormFieldModule, MatIconModule, MatSelectModule], templateUrl: './ordering.html', styleUrl: './ordering.css', }) @@ -36,6 +39,12 @@ export class Ordering { } } + setDirection(direction: orderDirType, event?: Event) { + event?.preventDefault(); + event?.stopPropagation(); + this.orderDir.set(direction); + } + orderChanged = output(); } diff --git a/src/app/shared/task-execution-inputs-panel/task-execution-inputs-panel.html b/src/app/shared/task-execution-inputs-panel/task-execution-inputs-panel.html index 489cd3a..b87aa36 100644 --- a/src/app/shared/task-execution-inputs-panel/task-execution-inputs-panel.html +++ b/src/app/shared/task-execution-inputs-panel/task-execution-inputs-panel.html @@ -27,16 +27,20 @@ }
- + + {{ requirement.fieldName || 'Authorization' }} + +
@@ -113,7 +114,8 @@ @if (output.isLong) { @@ -137,7 +139,7 @@

{{ outputModal.title }}

{{ outputModal.subtitle }}

- +
{{ outputModal.value }}
diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.ts b/src/app/shared/task-execution-viewer/task-execution-viewer.ts index 238c1b9..c249ca4 100644 --- a/src/app/shared/task-execution-viewer/task-execution-viewer.ts +++ b/src/app/shared/task-execution-viewer/task-execution-viewer.ts @@ -1,5 +1,8 @@ import { CommonModule } from '@angular/common'; import { Component, computed, effect, inject, input, OnDestroy, signal } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { FlowBlockConnection, FlowData } from '@models/flow'; import { getExecutionStatusGroup, @@ -25,7 +28,7 @@ type ExecutionOutputEntry = { @Component({ selector: 'app-task-execution-viewer', - imports: [CommonModule, ReteEditor, TaskExecutionInputsPanelComponent], + imports: [CommonModule, ReteEditor, TaskExecutionInputsPanelComponent, MatButtonModule, MatIconModule, MatTooltipModule], templateUrl: './task-execution-viewer.html', styleUrl: './task-execution-viewer.css', }) diff --git a/src/app/shared/tasks-executions-list/tasks-executions-list.css b/src/app/shared/tasks-executions-list/tasks-executions-list.css index 478abc9..c2f11a5 100644 --- a/src/app/shared/tasks-executions-list/tasks-executions-list.css +++ b/src/app/shared/tasks-executions-list/tasks-executions-list.css @@ -2,3 +2,137 @@ display: block; height: 100%; } + +.tasks-list-shell { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 8px; +} + +.tasks-list-search, +.tasks-list-filter, +.tasks-list-ordering { + padding: 16px 12px 0; +} + +.tasks-list-filter { + display: flex; + justify-content: center; +} + +.tasks-list-filter-group { + display: inline-flex; + width: auto; + max-width: 100%; +} + +.tasks-list-ordering { + padding-bottom: 16px; +} + +.tasks-list-items { + flex: 1; + overflow-y: auto; + padding: 10px 8px; +} + +.tasks-list-list { + padding: 0 !important; + background: transparent; +} + +.tasks-list-list-item { + height: auto !important; + padding: 0 !important; + margin-bottom: 10px; + overflow: visible !important; +} + +:host ::ng-deep .tasks-list-list-item .mdc-list-item__content, +:host ::ng-deep .tasks-list-list-item .mat-mdc-list-item-unscoped-content { + overflow: visible !important; +} + +.tasks-list-empty { + padding: 16px 8px; + font-size: 12px; + color: #64748b; + text-align: center; +} + +.tasks-list-item { + width: 100%; + padding: 12px; + border: 1px solid #cbd5e1; + border-radius: 14px; + background: + radial-gradient(circle at top right, rgba(16, 185, 129, 0.1), transparent 32%), + linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); + box-shadow: + 0 10px 24px rgba(15, 23, 42, 0.07), + inset 0 1px 0 rgba(255, 255, 255, 0.72); + text-align: left; + cursor: pointer; + transition: transform 0.15s ease, border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease; +} + +.tasks-list-item:hover { + transform: translateY(-3px); + border-color: #60a5fa; + background: + radial-gradient(circle at top right, rgba(96, 165, 250, 0.16), transparent 34%), + linear-gradient(180deg, #f8fbff 0%, #dbeafe 100%); + box-shadow: 0 18px 28px rgba(37, 99, 235, 0.14); +} + +.tasks-list-item-meta { + min-width: 0; +} + +.tasks-list-item-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + font-weight: 700; + color: #1e293b; +} + +.tasks-list-item-subtitle { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + color: #64748b; +} + +.tasks-list-status { + padding: 3px 9px; + border-radius: 999px; + border-width: 1px; + font-size: 10px; + font-weight: 600; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.52); +} + +.tasks-list-item-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-top: 10px; + font-size: 11px; + color: #64748b; + padding-top: 8px; + border-top: 1px solid rgba(148, 163, 184, 0.18); +} + +.tasks-list-item-actions { + display: flex; + align-items: center; + gap: 8px; +} diff --git a/src/app/shared/tasks-executions-list/tasks-executions-list.html b/src/app/shared/tasks-executions-list/tasks-executions-list.html index 609f94f..54c3a1f 100644 --- a/src/app/shared/tasks-executions-list/tasks-executions-list.html +++ b/src/app/shared/tasks-executions-list/tasks-executions-list.html @@ -1,42 +1,23 @@ -