messages styling, add conversation dropdown, show tooltip on filters
This commit is contained in:
parent
c3319a6e84
commit
0e9a0aa21e
39 changed files with 537 additions and 180 deletions
|
@ -46,7 +46,6 @@
|
|||
margin-bottom: 40px;
|
||||
margin-top: 10px;
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
width: 500px;
|
||||
max-width: 100%;
|
||||
|
||||
|
||||
|
@ -183,8 +182,6 @@
|
|||
min-width: 100vw !important;
|
||||
max-width: 100vw !important;
|
||||
position: static;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -223,6 +223,34 @@
|
|||
.conversation-container {
|
||||
padding-left: 12px;
|
||||
min-height: 90vh;
|
||||
|
||||
.actions {
|
||||
position: relative;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
width: 30%;
|
||||
float: right;
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: variables.$primary-color;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
button {
|
||||
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
|
@ -381,7 +409,7 @@
|
|||
}
|
||||
|
||||
.main-conversation-container {
|
||||
|
||||
|
||||
height: 100vh;
|
||||
padding-top: 88px;
|
||||
border-radius: 10px;
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
@use "shadows";
|
||||
|
||||
|
||||
app-main-button {
|
||||
button {
|
||||
background: shadows.$primary-color;
|
||||
color: shadows.$neutral-white;
|
||||
border-radius: shadows.$radius-main;
|
||||
padding: 1rem 2rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
|
||||
i {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
background: shadows.$main-bg-color-active;
|
||||
color: shadows.$main-color-active;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
@use "sass:color";
|
||||
@use "variables";
|
||||
@use "./variables";
|
||||
|
||||
|
||||
// state colors
|
||||
|
@ -31,7 +31,6 @@
|
|||
background: variables.$success-color;
|
||||
color: variables.$neutral-white;
|
||||
border-color: color.adjust(variables.$success-color, $lightness: -50%);
|
||||
|
||||
}
|
||||
|
||||
.is-error {
|
||||
|
|
|
@ -25,6 +25,5 @@
|
|||
@use '_states.scss';
|
||||
@use '_conversations.scss';
|
||||
@use '_layout_demo.scss';
|
||||
@use '_main_button.scss';
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,27 @@ const config: StorybookConfig = {
|
|||
generator: {filename: 'fonts/[name].[contenthash][ext]'},
|
||||
});
|
||||
|
||||
// Support for remixicon font files specifically
|
||||
config.module.rules.push({
|
||||
test: /remixicon.*\.(woff|woff2|eot|ttf|svg)$/,
|
||||
type: 'asset/resource',
|
||||
generator: {filename: 'fonts/[name].[contenthash][ext]'},
|
||||
});
|
||||
|
||||
// Add CSS loader rule to handle @font-face declarations
|
||||
config.module.rules.push({
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
url: true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// NOUVELLE SOLUTION : Configurer css-loader pour gérer les URLs
|
||||
const scssRules = config.module.rules.filter(rule => {
|
||||
if (rule && typeof rule === 'object' && 'test' in rule && rule.test) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type {Preview} from '@storybook/angular';
|
||||
import 'remixicon/fonts/remixicon.css';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
@use "sae-lib/src/styles/shadows.scss";
|
||||
@use "sae-lib/src/styles/states.scss";
|
||||
@use "sae-lib/src/styles/variables.scss";
|
||||
@use "sass:color";
|
||||
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
|
||||
button {
|
||||
background: shadows.$primary-color;
|
||||
color: shadows.$neutral-white;
|
||||
border-radius: shadows.$radius-main;
|
||||
padding: 1rem 2rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
|
||||
i {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
background: shadows.$main-bg-color-active;
|
||||
color: shadows.$main-color-active;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
|
||||
// state colors
|
||||
&.is-primary {
|
||||
background-color: variables.$primary-color;
|
||||
color: variables.$neutral-white;
|
||||
border-color: color.adjust(variables.$primary-color, $lightness: - 10%);
|
||||
}
|
||||
|
||||
&.is-secondary {
|
||||
background-color: variables.$secondary-color;
|
||||
color: variables.$neutral-white;
|
||||
}
|
||||
|
||||
&.is-warning {
|
||||
background: variables.$neutral-white;
|
||||
color: variables.$warning-color-text;
|
||||
border: 0;
|
||||
|
||||
}
|
||||
|
||||
&.is-info {
|
||||
background: variables.$info-color;
|
||||
color: variables.$neutral-white;
|
||||
border-color: color.adjust(variables.$info-color, $lightness: -50%);
|
||||
}
|
||||
|
||||
&.is-success {
|
||||
background: variables.$success-color;
|
||||
color: variables.$neutral-white;
|
||||
border-color: color.adjust(variables.$success-color, $lightness: -50%);
|
||||
}
|
||||
|
||||
&.is-error {
|
||||
background: rgba(variables.$danger-color, 10%);
|
||||
color: variables.$danger-color;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,16 +1,24 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { MainButton } from './main-button';
|
||||
import type {Meta, StoryObj} from '@storybook/angular';
|
||||
import {MainButton} from './main-button';
|
||||
import {moduleMetadata} from '@storybook/angular';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<MainButton> = {
|
||||
title: 'Components/MainButton',
|
||||
component: MainButton,
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [CommonModule],
|
||||
providers: []
|
||||
})
|
||||
],
|
||||
argTypes: {
|
||||
label: { control: 'text' },
|
||||
icon: { control: 'text' },
|
||||
kind: {
|
||||
control: 'select',
|
||||
label: {control: 'text'},
|
||||
icon: {control: 'text'},
|
||||
kind: {
|
||||
control: 'select',
|
||||
options: ['', 'primary', 'secondary', 'info', 'success', 'warning', 'danger'],
|
||||
description: 'Style du bouton'
|
||||
},
|
||||
|
@ -24,7 +32,7 @@ type Story = StoryObj<MainButton>;
|
|||
export const Primary: Story = {
|
||||
args: {
|
||||
label: 'Button',
|
||||
icon: 'ri-home-line',
|
||||
icon: 'home-line-2',
|
||||
kind: 'primary'
|
||||
},
|
||||
};
|
||||
|
@ -32,7 +40,7 @@ export const Primary: Story = {
|
|||
export const Secondary: Story = {
|
||||
args: {
|
||||
label: 'Secondary Button',
|
||||
icon: 'ri-settings-line',
|
||||
icon: 'settings-line',
|
||||
kind: 'secondary'
|
||||
},
|
||||
};
|
||||
|
@ -40,7 +48,7 @@ export const Secondary: Story = {
|
|||
export const Info: Story = {
|
||||
args: {
|
||||
label: 'Info Button',
|
||||
icon: 'ri-information-line',
|
||||
icon: 'information-line',
|
||||
kind: 'info'
|
||||
},
|
||||
};
|
||||
|
@ -48,7 +56,7 @@ export const Info: Story = {
|
|||
export const Success: Story = {
|
||||
args: {
|
||||
label: 'Success Button',
|
||||
icon: 'ri-check-line',
|
||||
icon: 'check-line',
|
||||
kind: 'success'
|
||||
},
|
||||
};
|
||||
|
@ -56,7 +64,7 @@ export const Success: Story = {
|
|||
export const Warning: Story = {
|
||||
args: {
|
||||
label: 'Warning Button',
|
||||
icon: 'ri-alert-line',
|
||||
icon: 'alert-line',
|
||||
kind: 'warning'
|
||||
},
|
||||
};
|
||||
|
@ -64,7 +72,7 @@ export const Warning: Story = {
|
|||
export const Danger: Story = {
|
||||
args: {
|
||||
label: 'Danger Button',
|
||||
icon: 'ri-close-circle-line',
|
||||
icon: 'close-circle-line',
|
||||
kind: 'danger'
|
||||
},
|
||||
};
|
||||
|
@ -80,7 +88,7 @@ export const WithoutIcon: Story = {
|
|||
export const WithIcon: Story = {
|
||||
args: {
|
||||
label: 'Button with icon',
|
||||
icon: 'ri-user-line',
|
||||
icon: 'user-line',
|
||||
kind: ''
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
import {Component, Input} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
export type ButtonKind = '' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'danger';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-button',
|
||||
imports: [],
|
||||
imports: [CommonModule],
|
||||
template: `
|
||||
<button class="is-{{kind}}">
|
||||
|
||||
|
@ -17,9 +18,7 @@ export type ButtonKind = '' | 'primary' | 'secondary' | 'info' | 'success' | 'wa
|
|||
</span>
|
||||
</button>
|
||||
`,
|
||||
styles: `
|
||||
|
||||
`
|
||||
styleUrl: './main-button.scss'
|
||||
})
|
||||
export class MainButton {
|
||||
|
|
@ -10,6 +10,19 @@
|
|||
<div class="demo-airwatch">
|
||||
<div class="layout-split">
|
||||
<nav aria-label="main navigation" class="navbar" role="navigation">
|
||||
<div class="navbar-start">
|
||||
<a (click)="toggleSidePanelConversationsList()" class="navbar-item aside-toggle-button">
|
||||
|
||||
|
||||
@if (appState.displayConversationListPanelLarge) {
|
||||
<i class="ri-sidebar-fold-line"></i>
|
||||
} @else {
|
||||
<i class="ri-sidebar-unfold-line"></i>
|
||||
}
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/#">
|
||||
<!-- <i class="ri-robot-2-fill"></i>-->
|
||||
|
@ -36,20 +49,8 @@
|
|||
</div>
|
||||
|
||||
<div class="navbar-menu" id="airwatchNavigation">
|
||||
<div class="navbar-start">
|
||||
<a (click)="toggleSidePanelConversationsList()" class="navbar-item aside-toggle-button">
|
||||
|
||||
|
||||
@if (appState.displayConversationListPanelLarge) {
|
||||
<i class="ri-sidebar-fold-line"></i>
|
||||
} @else {
|
||||
<i class="ri-sidebar-unfold-line"></i>
|
||||
}
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<a (click)="toggleSourcesPanel()" class="navbar-item aside-toggle-button">
|
||||
@if (appState.displaySourcesPanelLarge) {
|
||||
|
@ -162,6 +163,28 @@
|
|||
[ngClass]="{'is-expanded': ! conversation.visibleInput}">
|
||||
<!-- <i class="ri-pencil-line" (click)="toggleVisibleEditInput(conversation)"></i>-->
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="menu">
|
||||
<!-- menu-->
|
||||
<i class="ri-more-2-line"></i>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button (click)="pinConversation(conversation)">
|
||||
<i class="ri-pushpin-fill"></i>
|
||||
<span class="label">
|
||||
|
||||
pin
|
||||
</span>
|
||||
</button>
|
||||
<button (click)="copyConversationLink(conversation)">
|
||||
<i class="ri-link-m"></i>
|
||||
<span class="label">
|
||||
|
||||
copy
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="active-peak">
|
||||
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 7L0 13.9282V0.0717969L12 7Z" fill="#3B87CC" fill-opacity="0.1"/>
|
||||
|
@ -242,7 +265,7 @@
|
|||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="conversation-messages-container">
|
||||
|
||||
<app-time-separator></app-time-separator>
|
||||
@if (activeConversation && activeConversation.messages) {
|
||||
@for (message of activeConversation.messages; track message) {
|
||||
<app-message-box [message]="message" [kind]="message.kind"
|
||||
|
@ -253,15 +276,8 @@
|
|||
@if (appState.loading) {
|
||||
<app-loading-notification></app-loading-notification>
|
||||
}
|
||||
<!-- <app-time-separator></app-time-separator>-->
|
||||
|
||||
|
||||
<!-- <sae-alert-box _alertKind="error" message="">System error - I couldn’t process your request.-->
|
||||
<!-- Please-->
|
||||
<!-- try-->
|
||||
<!-- again in-->
|
||||
<!-- few moments-->
|
||||
<!-- </sae-alert-box>-->
|
||||
</div>
|
||||
<div class="conversation-bottom">
|
||||
<app-prompt-input></app-prompt-input>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@use "sae-lib/src/styles/variables";
|
||||
|
||||
.panel-more-inside {
|
||||
border-radius: 10px;
|
||||
background: #F5F5F5;
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { Chatbot } from './chatbot';
|
||||
import { Store, StoreModule } from '@ngrx/store';
|
||||
import { reducers } from '../reducers';
|
||||
import type {Meta, StoryObj} from '@storybook/angular';
|
||||
import {moduleMetadata} from '@storybook/angular';
|
||||
import {Chatbot} from './chatbot';
|
||||
import {Store, StoreModule} from '@ngrx/store';
|
||||
import {reducers} from '../reducers';
|
||||
import {ConversationsService} from '../services/conversations.service';
|
||||
import {ApiService} from '../services/api-service';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {TranslationService} from '../services/translation.service';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {PromptInput} from './prompt-input/prompt-input';
|
||||
import {MessageBox} from './message-box/message-box';
|
||||
import {NewInput} from './new-input/new-input';
|
||||
import {FeedbackButton} from './feedback-button/feedback-button';
|
||||
import {WarningBugs} from './warning-bugs/warning-bugs';
|
||||
import {VersionInfos} from './version-infos/version-infos';
|
||||
import {LoadingNotification} from './loading-notification/loading-notification';
|
||||
import {SourceBlock} from './source-block/source-block';
|
||||
import {ToolsOptions} from './tools-options/tools-options';
|
||||
|
||||
const appReducer = reducers.app;
|
||||
import { ConversationsService } from '../services/conversations.service';
|
||||
import { ApiService } from '../api-service';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { TranslationService } from '../services/translation.service';
|
||||
import { moduleMetadata } from '@storybook/angular';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { PromptInput } from './prompt-input/prompt-input';
|
||||
import { MessageBox } from './message-box/message-box';
|
||||
import { NewInput } from './new-input/new-input';
|
||||
import { FeedbackButton } from './feedback-button/feedback-button';
|
||||
import { WarningBugs } from './warning-bugs/warning-bugs';
|
||||
import { VersionInfos } from './version-infos/version-infos';
|
||||
import { LoadingNotification } from './loading-notification/loading-notification';
|
||||
import { SourceBlock } from './source-block/source-block';
|
||||
import { ToolsOptions } from './tools-options/tools-options';
|
||||
|
||||
const meta: Meta<Chatbot> = {
|
||||
title: 'App/Chatbot',
|
||||
|
@ -31,7 +31,7 @@ const meta: Meta<Chatbot> = {
|
|||
CommonModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
StoreModule.forRoot({ app: appReducer as any }),
|
||||
StoreModule.forRoot({app: appReducer as any}),
|
||||
PromptInput,
|
||||
MessageBox,
|
||||
NewInput,
|
||||
|
|
|
@ -14,11 +14,12 @@ import {ChatbotConversation, ChatbotSource, ConversationsService} from '../servi
|
|||
import {Store} from '@ngrx/store';
|
||||
import {ActionTypes, StateInterface} from '../reducers';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {ApiService} from '../api-service';
|
||||
import {ApiService} from '../services/api-service';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ChatbotMessage, ChatbotMessageKind} from '../services/chatbot.message.type';
|
||||
import {ToolsOptions} from './tools-options/tools-options';
|
||||
import {TranslationService} from '../services/translation.service';
|
||||
import {TimeSeparator} from './time-separator/time-separator';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chatbot',
|
||||
|
@ -34,6 +35,7 @@ import {TranslationService} from '../services/translation.service';
|
|||
FormsModule,
|
||||
CommonModule,
|
||||
ToolsOptions,
|
||||
TimeSeparator,
|
||||
],
|
||||
templateUrl: './chatbot.html',
|
||||
styleUrl: './chatbot.scss'
|
||||
|
@ -313,6 +315,15 @@ export class Chatbot implements AfterViewChecked {
|
|||
this.store.dispatch({type: ActionTypes.SWITCH_TO_NEXT_LANGUAGE});
|
||||
}
|
||||
|
||||
pinConversation(conversation: ChatbotConversation) {
|
||||
conversation.pinned = !conversation.pinned;
|
||||
}
|
||||
|
||||
copyConversationLink(conversation: ChatbotConversation) {
|
||||
console.log('todo copy conversation link', conversation);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to the bottom of the conversation container
|
||||
*/
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { ApiService } from '../../api-service';
|
||||
import { ActionTypes, StateInterface } from '../../reducers';
|
||||
import {Component} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ApiService} from '../../services/api-service';
|
||||
import {ActionTypes, StateInterface} from '../../reducers';
|
||||
|
||||
@Component({
|
||||
selector: 'app-feedback-button',
|
||||
|
@ -21,7 +21,8 @@ export class FeedbackButton {
|
|||
constructor(
|
||||
private store: Store<StateInterface>,
|
||||
private apiService: ApiService
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
|
||||
toggleModal() {
|
||||
this.isModalOpen = !this.isModalOpen;
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
<button (click)="editMessage()" class="button edit">
|
||||
<i class="ri-edit-box-line"></i>
|
||||
</button>
|
||||
} @else {
|
||||
<button (click)="toggleFullScreen()" class="button fullscreen">
|
||||
<i class="ri-fullscreen-line"></i>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class="user-infos ">
|
||||
|
@ -57,7 +61,16 @@
|
|||
<button (click)="bookmark()" class="button bookmark">
|
||||
<i class="ri-bookmark-line"></i>
|
||||
</button>
|
||||
<button (click)="toggleSources()" class="button sources">
|
||||
<i class="ri-book-2-fill"></i>
|
||||
see sources
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div [ngClass]="{'expanded': expanded}" class="expanded-message-fullscreen">
|
||||
@if (content) {
|
||||
<div [innerHTML]="sanitizedContent"></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
|
||||
|
@ -25,4 +28,21 @@
|
|||
margin-left: 39px;
|
||||
}
|
||||
|
||||
.message {
|
||||
|
||||
|
||||
.actions {
|
||||
.fullscreen {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.expanded-message-fullscreen {
|
||||
display: none;
|
||||
width: 50%;
|
||||
|
||||
&.is-visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
|
|||
import {Copy} from 'sae-lib/buttons/copy/copy';
|
||||
import {FeedbackButton} from '../feedback-button/feedback-button';
|
||||
import {ChatbotMessage} from '../../services/chatbot.message.type';
|
||||
import {NgClass} from '@angular/common';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {StateInterface} from '../../reducers';
|
||||
|
||||
|
||||
type MessageKind = "user" | "llm";
|
||||
|
@ -12,7 +15,8 @@ type MessageKind = "user" | "llm";
|
|||
selector: 'app-message-box',
|
||||
imports: [
|
||||
Copy,
|
||||
FeedbackButton
|
||||
FeedbackButton,
|
||||
NgClass
|
||||
],
|
||||
templateUrl: './message-box.html',
|
||||
styleUrl: './message-box.scss'
|
||||
|
@ -25,8 +29,10 @@ export class MessageBox implements OnChanges {
|
|||
@Input() message: ChatbotMessage = {} as ChatbotMessage;
|
||||
id: string = "00122121221312";
|
||||
sanitizedContent: SafeHtml = "";
|
||||
expanded: boolean = false;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
constructor(private sanitizer: DomSanitizer, private store: Store<StateInterface>) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['content']) {
|
||||
|
@ -50,4 +56,19 @@ export class MessageBox implements OnChanges {
|
|||
editMessage() {
|
||||
console.log("TODO editMessage")
|
||||
}
|
||||
|
||||
toggleSources() {
|
||||
console.log("TODO toggle sources")
|
||||
this.store.dispatch({
|
||||
type: 'UPDATE_APP',
|
||||
payload: {
|
||||
displaySourcesPanelLarge: !this.store.select(state => state.app.displaySourcesPanelLarge)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
console.log("TODO toggle fullscreen")
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
</div>
|
||||
|
||||
<app-prompt-input></app-prompt-input>
|
||||
<app-tools-options></app-tools-options>
|
||||
tools:
|
||||
<app-tools-options [hideDisabledButtons]="false"></app-tools-options>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { NewInput } from './new-input';
|
||||
import { moduleMetadata } from '@storybook/angular';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { PromptInput } from '../prompt-input/prompt-input';
|
||||
import { ToolsOptions } from '../tools-options/tools-options';
|
||||
import { Store, StoreModule } from '@ngrx/store';
|
||||
import { reducers } from '../../reducers';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { ApiService } from '../../api-service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import type {Meta, StoryObj} from '@storybook/angular';
|
||||
import {moduleMetadata} from '@storybook/angular';
|
||||
import {NewInput} from './new-input';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {PromptInput} from '../prompt-input/prompt-input';
|
||||
import {ToolsOptions} from '../tools-options/tools-options';
|
||||
import {Store, StoreModule} from '@ngrx/store';
|
||||
import {reducers} from '../../reducers';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {ApiService} from '../../services/api-service';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
|
||||
const appReducer = reducers.app;
|
||||
|
||||
|
@ -24,7 +24,7 @@ const meta: Meta<NewInput> = {
|
|||
PromptInput,
|
||||
ToolsOptions,
|
||||
HttpClientModule,
|
||||
StoreModule.forRoot({ app: appReducer as any })
|
||||
StoreModule.forRoot({app: appReducer as any})
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
|
@ -36,8 +36,8 @@ const meta: Meta<NewInput> = {
|
|||
backgrounds: {
|
||||
default: 'light',
|
||||
values: [
|
||||
{ name: 'light', value: '#f5f5f5' },
|
||||
{ name: 'dark', value: '#333' },
|
||||
{name: 'light', value: '#f5f5f5'},
|
||||
{name: 'dark', value: '#333'},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export const DarkTheme: Story = {
|
|||
// Propriétés par défaut
|
||||
},
|
||||
parameters: {
|
||||
backgrounds: { default: 'dark' },
|
||||
backgrounds: {default: 'dark'},
|
||||
store: {
|
||||
init: (store: Store) => {
|
||||
store.dispatch({
|
||||
|
|
|
@ -3,7 +3,7 @@ import {Component} from '@angular/core';
|
|||
import {inputModeChoicesType} from '../../services/AirWatchState';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ActionTypes, StateInterface} from '../../reducers';
|
||||
import {ApiService} from '../../api-service';
|
||||
import {ApiService} from '../../services/api-service';
|
||||
import {NgClass} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
<input (keydown)="handleKeyEvent($event)" [(ngModel)]="userInput"
|
||||
placeholder="Ask questions, request or a web search" type="text">
|
||||
|
||||
<!-- <app-prompt-indicator></app-prompt-indicator>-->
|
||||
|
||||
<button (click)="submitMessage()" class="submit">
|
||||
<i class="ri-send-plane-2-line"></i>
|
||||
|
|
|
@ -14,11 +14,16 @@
|
|||
|
||||
input {
|
||||
width: 668px;
|
||||
max-width: 100%;
|
||||
border: 0;
|
||||
padding: 10px;
|
||||
@extend .is-shadowed !optional;
|
||||
float: left;
|
||||
|
||||
.is-expanded-right & {
|
||||
width: 968px;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { PromptInput } from './prompt-input';
|
||||
import { moduleMetadata } from '@storybook/angular';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Store, StoreModule } from '@ngrx/store';
|
||||
import { reducers } from '../../reducers';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { ApiService } from '../../api-service';
|
||||
import type {Meta, StoryObj} from '@storybook/angular';
|
||||
import {moduleMetadata} from '@storybook/angular';
|
||||
import {PromptInput} from './prompt-input';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {Store, StoreModule} from '@ngrx/store';
|
||||
import {reducers} from '../../reducers';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {ApiService} from '../../services/api-service';
|
||||
|
||||
const appReducer = reducers.app;
|
||||
|
||||
|
@ -20,7 +20,7 @@ const meta: Meta<PromptInput> = {
|
|||
CommonModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
StoreModule.forRoot({ app: appReducer as any })
|
||||
StoreModule.forRoot({app: appReducer as any})
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
|
|
|
@ -5,13 +5,12 @@ import {Store} from '@ngrx/store';
|
|||
import {ActionTypes, StateInterface} from '../../reducers';
|
||||
import {ChatbotMessage} from '../../services/chatbot.message.type';
|
||||
import {ChatbotConversation} from '../../services/conversations.service';
|
||||
import {ApiService, OpenMeteoResponse} from '../../api-service';
|
||||
import {ApiService, OpenMeteoResponse} from '../../services/api-service';
|
||||
import {interval, Subscription} from 'rxjs';
|
||||
import {PromptIndicator} from '../prompt-indicator/prompt-indicator';
|
||||
|
||||
@Component({
|
||||
selector: 'app-prompt-input',
|
||||
imports: [CommonModule, FormsModule, PromptIndicator],
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './prompt-input.html',
|
||||
styleUrl: './prompt-input.scss'
|
||||
})
|
||||
|
|
|
@ -4,15 +4,21 @@
|
|||
color: grey;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
display: block;
|
||||
|
||||
.column {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
|
||||
&.time-ago {
|
||||
margin-top: -32px;
|
||||
margin-bottom: 16px;
|
||||
text-align: center;
|
||||
width: auto;
|
||||
padding: 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.time-ago {
|
||||
margin-top: -1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.line {
|
||||
border-bottom: 1px solid grey;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<button (click)="toggleState()"
|
||||
class="togglable-button {{kind}} {{active ? 'is-active' : ''}}">
|
||||
class="togglable-button {{kind}} {{active ? 'is-active' : ''}}"
|
||||
[title]="tooltip">
|
||||
@if (icon) {
|
||||
<span class="icon ri-{{icon}}"></span>
|
||||
}
|
||||
|
|
|
@ -13,6 +13,27 @@
|
|||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
// Custom tooltip styling
|
||||
&[title] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: black;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
z-index: 100;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
&:first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ export class ToggleButton {
|
|||
@Input() kind: ToggleButtonKind = "local";
|
||||
@Input() active: boolean = false;
|
||||
@Input() icon: string = "";
|
||||
@Input() tooltip: string = "";
|
||||
@Output() stateChanged = new EventEmitter<boolean>();
|
||||
|
||||
toggleState() {
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
<div class="tools-options {{alignLeft ? 'has-text-left' : ''}}">
|
||||
@for (filter of filters; track filter.name) {
|
||||
@if (filter.enabled && hideDisabledButtons) {
|
||||
@if (hideDisabledButtons) {
|
||||
@if (filter.enabled) {
|
||||
|
||||
<app-toggle-button
|
||||
[active]="filter.enabled"
|
||||
[icon]="filter.kind === 'local' ? 'database-2-line' : 'google-line'"
|
||||
[kind]="filter.kind"
|
||||
[label]="filter.name"
|
||||
[tooltip]="getTooltipForFilter(filter)"
|
||||
(stateChanged)="toggleFilter(filter, $event)">
|
||||
</app-toggle-button>
|
||||
}
|
||||
} @else {
|
||||
<app-toggle-button
|
||||
[active]="filter.enabled"
|
||||
[icon]="filter.kind === 'local' ? 'database-2-line' : 'google-line'"
|
||||
[kind]="filter.kind"
|
||||
[label]="filter.name"
|
||||
[tooltip]="getTooltipForFilter(filter)"
|
||||
(stateChanged)="toggleFilter(filter, $event)">
|
||||
</app-toggle-button>
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ export class ToolsOptions {
|
|||
|
||||
constructor(private store: Store<StateInterface>) {
|
||||
this.store.select(state => state.app.filters).subscribe(filters => {
|
||||
|
||||
console.log("filters", filters)
|
||||
this.filters = filters;
|
||||
});
|
||||
}
|
||||
|
@ -44,4 +46,10 @@ export class ToolsOptions {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTooltipForFilter(filter: filterType): string {
|
||||
// Generate descriptive tooltips based on filter name and kind
|
||||
const kindDescription = filter.kind === 'local' ? 'Données internes' : 'Données externes';
|
||||
return `${filter.name} - ${kindDescription}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { ColorDisplay } from './color-display';
|
||||
import { moduleMetadata } from '@storybook/angular';
|
||||
import { NgStyle } from '@angular/common';
|
||||
|
||||
const meta: Meta<ColorDisplay> = {
|
||||
title: 'Components/ColorDisplay',
|
||||
component: ColorDisplay,
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [NgStyle],
|
||||
providers: []
|
||||
})
|
||||
],
|
||||
argTypes: {
|
||||
hexaCode: {
|
||||
control: 'text',
|
||||
description: "Code hexadécimal de la couleur"
|
||||
},
|
||||
name: {
|
||||
control: 'text',
|
||||
description: "Nom de la couleur"
|
||||
},
|
||||
backgroundColor: {
|
||||
control: 'text',
|
||||
description: "Couleur de fond (si différente du code hexa)"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<ColorDisplay>;
|
||||
|
||||
export const Red: Story = {
|
||||
args: {
|
||||
hexaCode: '#cc0000',
|
||||
name: 'Rouge',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const Blue: Story = {
|
||||
args: {
|
||||
hexaCode: '#0000cc',
|
||||
name: 'Bleu',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const Green: Story = {
|
||||
args: {
|
||||
hexaCode: '#00cc00',
|
||||
name: 'Vert',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const Yellow: Story = {
|
||||
args: {
|
||||
hexaCode: '#ffcc00',
|
||||
name: 'Jaune',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const Purple: Story = {
|
||||
args: {
|
||||
hexaCode: '#9900cc',
|
||||
name: 'Violet',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const Orange: Story = {
|
||||
args: {
|
||||
hexaCode: '#ff6600',
|
||||
name: 'Orange',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const Black: Story = {
|
||||
args: {
|
||||
hexaCode: '#000000',
|
||||
name: 'Noir',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
||||
|
||||
export const White: Story = {
|
||||
args: {
|
||||
hexaCode: '#ffffff',
|
||||
name: 'Blanc',
|
||||
backgroundColor: ''
|
||||
}
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {MainButton} from '../../main-button/main-button';
|
||||
import {MainButton} from '../../buttons/main-button/main-button';
|
||||
import {AlertBox} from 'sae-lib/alert-box/alert-box';
|
||||
|
||||
@Component({
|
||||
|
|
|
@ -6,17 +6,26 @@
|
|||
Tester les actions de l'api
|
||||
|
||||
<br>
|
||||
<button class="button">
|
||||
appel de login
|
||||
</button>
|
||||
<app-main-button
|
||||
(click)="login()" class="button" label='appel de login'>
|
||||
</app-main-button>
|
||||
<br>
|
||||
<button class="button">
|
||||
envoi de message
|
||||
</button>
|
||||
<app-main-button (click)="sendMessage()" class="button" label='envoi de message'>
|
||||
|
||||
</app-main-button>
|
||||
<br>
|
||||
<button class="button">
|
||||
envoi de fichier
|
||||
</button>
|
||||
<app-main-button (click)="sendFeedback()" class="button" label='envoi de feedback'>
|
||||
|
||||
</app-main-button>
|
||||
<br>
|
||||
<app-main-button (click)="sendFile()" class="button" label='envoi de fichier'>
|
||||
|
||||
</app-main-button>
|
||||
</div>
|
||||
<div class="column">
|
||||
<pre>
|
||||
{{ response }}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,24 +4,3 @@
|
|||
background: #c2d1dc;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: 1px solid #005AA2;
|
||||
color: #005AA2;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 100px;
|
||||
background: white;
|
||||
padding: 16px 40px;
|
||||
border-radius: 10px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #005AA2;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,46 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
import {ApiService} from '../../services/api-service';
|
||||
import {MainButton} from '../../buttons/main-button/main-button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-testing-api',
|
||||
imports: [],
|
||||
imports: [
|
||||
MainButton
|
||||
],
|
||||
templateUrl: './testing-api.html',
|
||||
styleUrl: './testing-api.scss'
|
||||
})
|
||||
export class TestingApi {
|
||||
|
||||
public response: any = '';
|
||||
|
||||
constructor(private apiservice: ApiService) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
login() {
|
||||
this.apiservice.fetchLogin().then(res => {
|
||||
console.log(res);
|
||||
this.response = res;
|
||||
})
|
||||
}
|
||||
|
||||
sendFeedback() {
|
||||
console.log('sendFeedback');
|
||||
this.apiservice.fetchEndPoint('feedback').then(res => {
|
||||
console.log(res);
|
||||
this.response = res;
|
||||
})
|
||||
}
|
||||
|
||||
sendMessage() {
|
||||
console.log('sendMessage');
|
||||
}
|
||||
|
||||
sendFile() {
|
||||
console.log('sendFile');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http';
|
|||
import {from, Observable, Subject, throwError} from 'rxjs';
|
||||
import {catchError, finalize, takeUntil, tap} from 'rxjs/operators';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ActionTypes, StateInterface} from './reducers';
|
||||
import {ChatbotMessage} from './services/chatbot.message.type';
|
||||
import {ActionTypes, StateInterface} from '../reducers';
|
||||
import {ChatbotMessage} from './chatbot.message.type';
|
||||
|
||||
/**
|
||||
* Interface for Open-Meteo API response
|
||||
|
@ -89,14 +89,20 @@ export class ApiService {
|
|||
constructor(private http: HttpClient, private store: Store<StateInterface>) {
|
||||
}
|
||||
|
||||
async fetchEndPoint(kind: string, subKind: string): Promise<Observable<Object>> {
|
||||
async fetchLogin() {
|
||||
console.log("fetchLogin");
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
});
|
||||
}
|
||||
|
||||
async fetchEndPoint(kind: string = '', subKind: string = ''): Promise<Observable<Object>> {
|
||||
// Record the start time for measuring response time
|
||||
const startTime = Date.now();
|
||||
|
||||
let fetchUrl: string = `${this.BASE_URL}/api/v1`
|
||||
if (undefined !== this.ENDPOINTS.main.v1[kind]) {
|
||||
if (null !== this.ENDPOINTS.main.v1[kind]) {
|
||||
fetchUrl += this.ENDPOINTS.main.v1[kind]
|
||||
if (undefined !== this.ENDPOINTS.main.v1[kind][subKind]) {
|
||||
if (null !== this.ENDPOINTS.main.v1[kind][subKind]) {
|
||||
fetchUrl += '/' + this.ENDPOINTS.main.v1[kind][subKind]
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import {type AfterViewInit, Component} from '@angular/core';
|
||||
import {ShepherdService} from 'angular-shepherd';
|
||||
import {MainButton} from '../main-button/main-button';
|
||||
import {MainButton} from '../buttons/main-button/main-button';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {ActionTypes, StateInterface} from '../reducers';
|
||||
import {TranslationService} from '../services/translation.service';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue