messages styling, add conversation dropdown, show tooltip on filters

This commit is contained in:
Tykayn 2025-08-18 18:14:22 +02:00 committed by tykayn
parent c3319a6e84
commit 0e9a0aa21e
39 changed files with 537 additions and 180 deletions

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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 {

View file

@ -25,6 +25,5 @@
@use '_states.scss';
@use '_conversations.scss';
@use '_layout_demo.scss';
@use '_main_button.scss';

View file

@ -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) {

View file

@ -1,4 +1,5 @@
import type {Preview} from '@storybook/angular';
import 'remixicon/fonts/remixicon.css';
const preview: Preview = {
parameters: {

View file

@ -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;
}
}
}

View file

@ -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: ''
},
};
};

View file

@ -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 {

View file

@ -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 couldnt process your request.-->
<!-- Please-->
<!-- try-->
<!-- again in-->
<!-- few moments-->
<!-- </sae-alert-box>-->
</div>
<div class="conversation-bottom">
<app-prompt-input></app-prompt-input>

View file

@ -1,3 +1,5 @@
@use "sae-lib/src/styles/variables";
.panel-more-inside {
border-radius: 10px;
background: #F5F5F5;

View file

@ -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,

View file

@ -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
*/

View file

@ -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;

View file

@ -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>

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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>

View file

@ -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({

View file

@ -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({

View file

@ -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>

View file

@ -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;
}

View file

@ -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,

View file

@ -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'
})

View file

@ -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;

View file

@ -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>
}

View file

@ -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;
}

View file

@ -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() {

View file

@ -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>
}

View file

@ -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}`;
}
}

View file

@ -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: ''
}
};

View file

@ -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({

View file

@ -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>

View file

@ -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;
}
}

View file

@ -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');
}
}

View file

@ -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]
}
}

View file

@ -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';