add options dropdown

This commit is contained in:
Tykayn 2025-08-19 16:19:55 +02:00 committed by tykayn
parent e6d85c7257
commit 036cda1431
15 changed files with 357 additions and 29 deletions

View file

@ -119,13 +119,13 @@
@if (appState.displayConversationListPanelLarge) {
<div class="search-button is-expanded is-clickable">
<i class="ri-search-line"></i>
<!-- <div class="search-button is-expanded is-clickable">-->
<!-- <i class="ri-search-line"></i>-->
<input type="text" class="no-borders" placeholder="Search a chat" value="blah">
<i class="funnel ri-filter-3-line"></i>
<!-- <input type="text" class="no-borders" placeholder="Search a chat" value="blah">-->
<!-- <i class="funnel ri-filter-3-line"></i>-->
</div>
<!-- </div>-->
} @else {
<div class="search-button is-clickable">

View file

@ -0,0 +1,131 @@
import type { Meta, StoryObj } from '@storybook/angular';
import { FeedbackButton } from './feedback-button';
import { moduleMetadata } from '@storybook/angular';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Store, StoreModule } from '@ngrx/store';
import { ApiService } from '../../services/api-service';
import { reducers } from '../../reducers';
// Mock ApiService
class MockApiService {
async sendUserFeedback(feedback: string): Promise<any> {
// Simulate API call
return new Promise((resolve) => {
setTimeout(() => {
resolve({ success: true });
}, 1000);
});
}
}
const meta: Meta<FeedbackButton> = {
title: 'Components/FeedbackButton',
component: FeedbackButton,
tags: ['autodocs'],
decorators: [
moduleMetadata({
imports: [
CommonModule,
FormsModule,
StoreModule.forRoot(reducers)
],
providers: [
Store,
{ provide: ApiService, useClass: MockApiService }
]
})
],
argTypes: {
isModalOpen: {
control: 'boolean',
description: 'Whether the feedback modal is open'
},
feedbackText: {
control: 'text',
description: 'The feedback text entered by the user'
},
isSubmitting: {
control: 'boolean',
description: 'Whether the feedback is being submitted'
},
submitSuccess: {
control: 'boolean',
description: 'Whether the feedback was submitted successfully'
},
submitError: {
control: 'boolean',
description: 'Whether there was an error submitting the feedback'
}
}
};
export default meta;
type Story = StoryObj<FeedbackButton>;
// Default state - just the button
export const Default: Story = {
args: {
isModalOpen: false,
feedbackText: '',
isSubmitting: false,
submitSuccess: false,
submitError: false
}
};
// Modal open state
export const ModalOpen: Story = {
args: {
isModalOpen: true,
feedbackText: '',
isSubmitting: false,
submitSuccess: false,
submitError: false
}
};
// Modal with text entered
export const WithFeedbackText: Story = {
args: {
isModalOpen: true,
feedbackText: 'This is some feedback text that the user has entered.',
isSubmitting: false,
submitSuccess: false,
submitError: false
}
};
// Submitting state
export const Submitting: Story = {
args: {
isModalOpen: true,
feedbackText: 'This is some feedback text that the user has entered.',
isSubmitting: true,
submitSuccess: false,
submitError: false
}
};
// Success state
export const SubmitSuccess: Story = {
args: {
isModalOpen: true,
feedbackText: 'This is some feedback text that the user has entered.',
isSubmitting: false,
submitSuccess: true,
submitError: false
}
};
// Error state
export const SubmitError: Story = {
args: {
isModalOpen: true,
feedbackText: 'This is some feedback text that the user has entered.',
isSubmitting: false,
submitSuccess: false,
submitError: true
}
};

View file

@ -25,30 +25,30 @@
}
.user-more-infos {
margin-top: -35px;
margin-left: 39px;
}
.message {
.user-more-infos {
margin-top: -35px;
margin-left: 39px;
}
&.user {
background: #232432;
color: #8b8ecf;
background: white;
color: #000;
}
&.llm {
//background: #232432;
.message-content {
color: #8b8ecf;
color: #000;
}
.actions {
.button {
background: #0d0e15;
color: grey;
background: rgba(59, 135, 204, 0.7);
color: black;
}
}

View file

@ -57,7 +57,19 @@ export const AIMessage: Story = {
})
}
};
export const AIMessageExpanded: Story = {
args: {
kind: 'llm',
content: 'Je suis un assistant IA qui répond à vos questions. Voici une réponse détaillée qui peut contenir du <strong>HTML</strong> et des listes:<ul><li>Point 1</li><li>Point 2</li></ul>',
message: new ChatbotMessage({
kind: 'llm',
user: {},
content: 'Je suis un assistant IA qui répond à vos questions. Voici une réponse détaillée qui peut contenir du <strong>HTML</strong> et des listes:<ul><li>Point 1</li><li>Point 2</li></ul>',
name: 'Assistant'
}),
expanded: true,
}
};
export const LongAIMessage: Story = {
args: {
kind: 'llm',

View file

@ -1,22 +1,16 @@
<div class="new-input">
<div class="main-conversation-container">
<!-- new input-->
<div class="welcome-text">
<div class="welcome-icon">
<!-- <i class="ri-robot-2-line"></i>-->
<img alt="chatbot image" src="/chatbot.png">
</div>
How can we
<span class="emphasis">
assist
</span>
assist
</span>
you today?
</div>
<app-prompt-input></app-prompt-input>
tools:
<app-tools-options [hideDisabledButtons]="false"></app-tools-options>
<app-prompt-input></app-prompt-input>
</div>
</div>

View file

@ -0,0 +1,21 @@
<div class="options-dropdown is-mode-{{mode}}" >
<div class="button">
<i class="ri-settings-2-line "></i>
<div class="indicator"></div>
</div>
<div class="dropwdown">
<div class="dropdown-item" (click)="changeModeOption('auto')">
<i class="ri-settings-2-line "></i>
<div class="label">
Auto mode
</div>
</div>
<div class="dropdown-item" (click)="changeModeOption('structured')">
<i class="ri-settings-2-line "></i>
<div class="label">
Structured mode
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,24 @@
.options-dropdown{
padding: 10px;
.button{
padding: 6px;
&:hover{
background: cornflowerblue;
}
}
.is-mode-structured,
.is-mode-other,
{
background: red;
border-radius: 100px;
width: 20px;
position: relative;
left: 10px;
top: 10px;
}
.dropdown-item{
background: white;
}
}

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OptionsDropdown } from './options-dropdown';
describe('OptionsDropdown', () => {
let component: OptionsDropdown;
let fixture: ComponentFixture<OptionsDropdown>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OptionsDropdown]
})
.compileComponents();
fixture = TestBed.createComponent(OptionsDropdown);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,17 @@
import {Component, Input} from '@angular/core';
import {NgClass} from '@angular/common';
@Component({
selector: 'app-options-dropdown',
templateUrl: './options-dropdown.html',
styleUrl: './options-dropdown.scss'
})
export class OptionsDropdown {
@Input() public mode: 'auto' | 'structured' = 'auto'
changeModeOption(mode: 'auto' | 'structured') {
this.mode = mode;
}
}

View file

@ -9,6 +9,7 @@
placeholder="Ask questions, request or a web search" type="text">
<app-options-dropdown></app-options-dropdown>
<button (click)="submitMessage()" class="submit">
<i class="ri-send-plane-2-line"></i>
</button>

View file

@ -7,10 +7,11 @@ import {ChatbotMessage} from '../../services/chatbot.message.type';
import {ChatbotConversation} from '../../services/conversations.service';
import {ApiService, OpenMeteoResponse} from '../../services/api-service';
import {interval, Subscription} from 'rxjs';
import {OptionsDropdown} from '../options-dropdown/options-dropdown';
@Component({
selector: 'app-prompt-input',
imports: [CommonModule, FormsModule],
imports: [CommonModule, FormsModule, OptionsDropdown],
templateUrl: './prompt-input.html',
styleUrl: './prompt-input.scss'
})

View file

@ -10,11 +10,19 @@
padding: 0;
width: auto;
&.time-ago {
color: #1E1F22;
font-size: 10px;
font-style: normal;
font-weight: 400;
line-height: 8px;
margin-top: -32px;
margin-bottom: 16px;
text-align: center;
width: auto;
width: 100px !important;
padding: 16px 0;
}
}

View file

@ -4,4 +4,11 @@
padding-top: 1rem;
border-top: solid 0.5px #b7b7b7;
app-toggle-button {
margin-bottom: 10px;
display: inline-block;
+ app-toggle-button {
margin-left: 10px;
}
}
}

View file

@ -3,5 +3,5 @@
message="System error - I couldn't process your request. Please try again in few moments"></sae-alert-box>
} @else {
<sae-alert-box _alertKind="warning"
message="AI can make mistakes - consider verifying important information."></sae-alert-box>
message="<strong>AI can make mistakes</strong> - consider verifying important information."></sae-alert-box>
}

View file

@ -0,0 +1,89 @@
import type { Meta, StoryObj } from '@storybook/angular';
import { AlertBox } from 'sae-lib/alert-box/alert-box';
import { moduleMetadata } from '@storybook/angular';
import { CommonModule } from '@angular/common';
const meta: Meta<AlertBox> = {
title: 'Components/AlertBox',
component: AlertBox,
tags: ['autodocs'],
decorators: [
moduleMetadata({
imports: [CommonModule],
providers: []
})
],
argTypes: {
message: {
control: 'text',
description: 'Message à afficher dans l\'alerte'
},
_alertKind: {
control: 'select',
options: ['info', 'success', 'primary', 'secondary', 'warning', 'danger', 'error'],
description: 'Type d\'alerte'
}
},
};
export default meta;
type Story = StoryObj<AlertBox>;
export const Warning: Story = {
args: {
message: 'Ceci est un message d\'avertissement',
_alertKind: 'warning'
},
};
export const Success: Story = {
args: {
message: 'Opération réussie !',
_alertKind: 'success'
},
};
export const Info: Story = {
args: {
message: 'Information importante',
_alertKind: 'info'
},
};
export const Primary: Story = {
args: {
message: 'Message principal',
_alertKind: 'primary'
},
};
export const Secondary: Story = {
args: {
message: 'Message secondaire',
_alertKind: 'secondary'
},
};
export const Danger: Story = {
args: {
message: 'Attention danger !',
_alertKind: 'danger'
},
};
export const Error: Story = {
args: {
message: 'Une erreur est survenue',
_alertKind: 'error'
},
};
export const WithContent: Story = {
args: {
_alertKind: 'warning'
},
render: (args) => ({
props: args,
template: `<sae-alert-box [_alertKind]="'warning'">Contenu personnalisé via ng-content</sae-alert-box>`
})
};