up feedback button, docker frontend csc

This commit is contained in:
Tykayn 2025-10-06 17:31:01 +02:00 committed by tykayn
parent 7ea3a0aff0
commit 09ba6d3e77
7 changed files with 476 additions and 404 deletions

View file

@ -1,179 +1,184 @@
.feedback-button { @use 'sae-lib/src/styles/variables' as variables;
background: #ecf3fa;
color: #083b7d;
padding: 12px;
border-radius: 8px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
transform: rotate(270deg);
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
margin-right: -35px; :host {
position: fixed;
right: 0;
top: 197px;
z-index: 100;
&:hover { .feedback-button {
background: #d9e8f6; background: #ecf3fa;
}
i {
font-size: 16px;
}
}
// Modal styles
.feedback-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.feedback-modal {
background: white;
border-radius: 8px;
width: 90%;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
max-height: 90vh;
overflow: hidden;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #eee;
h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #083b7d; color: #083b7d;
} padding: 12px;
border-radius: 8px;
.close-button { border-bottom-left-radius: 0;
background: none; border-bottom-right-radius: 0;
border: none; transform: rotate(270deg);
cursor: pointer; cursor: pointer;
font-size: 20px; display: flex;
color: #666; align-items: center;
padding: 4px; gap: 8px;
margin-right: -35px;
position: fixed;
right: 0;
top: 197px;
z-index: 100;
&:hover { &:hover {
color: #333; background: #d9e8f6;
} }
}
}
.modal-body {
padding: 20px;
overflow-y: auto;
.modal-description {
margin-bottom: 16px;
color: #555;
}
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
font-family: inherit;
font-size: 14px;
&:focus {
outline: none;
border-color: #083b7d;
}
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
}
.success-message, .error-message {
margin-top: 16px;
padding: 10px;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
i { i {
font-size: 18px; font-size: 16px;
} }
} }
.success-message { // Modal styles
background-color: #e6f7e6; .feedback-modal-overlay {
color: #2e7d32; position: fixed;
} top: 0;
left: 0;
.error-message { right: 0;
background-color: #fdecea; bottom: 0;
color: #d32f2f; background-color: rgba(0, 0, 0, 0.5);
}
}
.modal-footer {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
button {
padding: 8px 16px;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.cancel-button {
background: none;
border: 1px solid #ddd;
color: #555;
&:hover:not(:disabled) {
background-color: #f5f5f5;
}
}
.submit-button {
background-color: #083b7d;
color: white;
border: none;
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
gap: 8px; z-index: 1000;
}
&:hover:not(:disabled) { .feedback-modal {
background-color: #062c5e; background: white;
border-radius: 8px;
width: 90%;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
max-height: 90vh;
overflow: hidden;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #eee;
h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #083b7d;
}
.close-button {
background: none;
border: none;
cursor: pointer;
font-size: 20px;
color: #666;
padding: 4px;
&:hover {
color: #333;
}
} }
} }
.modal-body {
padding: 20px;
overflow-y: auto;
.modal-description {
margin-bottom: 16px;
color: #555;
}
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
font-family: inherit;
font-size: 14px;
&:focus {
outline: none;
border-color: #083b7d;
}
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
}
.success-message, .error-message {
margin-top: 16px;
padding: 10px;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
i {
font-size: 18px;
}
}
.success-message {
background-color: #e6f7e6;
color: #2e7d32;
}
.error-message {
background-color: #fdecea;
color: #d32f2f;
}
}
.modal-footer {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
button {
padding: 8px 16px;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.cancel-button {
background: none;
border: 1px solid #ddd;
color: #555;
&:hover:not(:disabled) {
background-color: #f5f5f5;
}
}
.submit-button {
background-color: #083b7d;
color: white;
border: none;
display: flex;
align-items: center;
gap: 8px;
&:hover:not(:disabled) {
background-color: #062c5e;
}
}
}
} }

View file

@ -30,7 +30,6 @@
<div class="navbar-end"> <div class="navbar-end">
<a class="navbar-item" routerLink="answer" routerLinkActive="active-link">answer </a> <a class="navbar-item" routerLink="answer" routerLinkActive="active-link">answer </a>
</div> </div>
</div> </div>
</nav> </nav>

View file

@ -6,7 +6,7 @@ services:
context: ./frontend context: ./frontend
dockerfile: ./frontend-dockerfile.yml dockerfile: ./frontend-dockerfile.yml
ports: ports:
- "4200:4200" - "80:80"
restart: unless-stopped restart: unless-stopped
container_name: frontend-app container_name: frontend-app

View file

@ -1,30 +1,26 @@
# dockerfile # dockerfile
FROM node:24 FROM node:24
# Définir le répertoire de travail # Définir le répertoire de travail
WORKDIR /app WORKDIR /app
# Définir une variable d'environnement pour le chemin de la librairie Angular # Définir une variable d'environnement pour le chemin de la librairie Angular
ENV LIB_PATH=../sae-csc ENV LIB_PATH=../sae-csc
# Copier les fichiers package.json et package-lock.json de la librairie
# Copier les fichiers package.json et package-lock.json de la librairie
COPY ${LIB_PATH}/package*.json ./ COPY ${LIB_PATH}/package*.json ./
# Installer les dépendances npm # Installer les dépendances npm
RUN npm install RUN npm install
# Copier le reste des sources (lib Angular + config Storybook) # Copier le reste des sources (lib Angular + config Storybook)
COPY ${LIB_PATH}/ . COPY ${LIB_PATH}/ .
# Construire l'application Angular # Construire l'application Angular
RUN npm run build -- --configuration=production RUN npm run build -- --configuration=production
# Installer un serveur HTTP pour servir l'application (par exemple, serve) from nginx:1.27-alpine
RUN npm install -g serve RUN rm -rf /usr/share/nginx/html/*
COPY --from=build /app/dist/implem /usr/share/nginx/html
# Exposer le port 4200 (ou 80 si vous préférez) EXPOSE 80
EXPOSE 4200 CMD ["nginx","-g","daemon off;"]
# Commande pour servir l'application Angular buildée
CMD ["serve", "-s", "dist", "-l", "4200"]

View file

@ -1,4 +1,6 @@
<button (click)="toggleModal()" class="feedback-button"> <button (click)="toggleModal()" [ngClass]="{
'is-active': isModalOpen
}" class="feedback-button">
<span class="text"> <span class="text">
Feedback Feedback
</span> </span>
@ -8,14 +10,40 @@
<!-- Feedback Modal --> <!-- Feedback Modal -->
@if (isModalOpen) { @if (isModalOpen) {
<div class="feedback-modal-overlay"> <div class="feedback-modal-overlay">
<div class="feedback-modal">
<div class="modal-header">
<h3>Share us your feedback !</h3>
<!-- <button class="close-button" (click)="toggleModal()">-->
<!-- <i class="ri-close-line"></i>-->
<!-- </button>-->
<sae-m-button [label]="'Soumettre'" [kind]="'primary'" [size]="'large'"></sae-m-button> <div class="feedback-modal">
<button (click)="toggleModal()" [ngClass]="{
'is-active': isModalOpen
}" class="feedback-button">
<span class="text">
Feedback
</span>
<i class="ri-message-2-line"></i>
</button>
<div class="modal-header">
<div class="side">
<h3>Share us your feedback !</h3>
<div class="star-row">
<i class="ri-star-line"></i>
<i class="ri-star-line"></i>
<i class="ri-star-line"></i>
<i class="ri-star-line"></i>
<i class="ri-star-line"></i>
</div>
</div>
<div class="side">
<sae-m-button (click)="toggleModal()" [kind]="'secondary'" [label]="'retour'"
[size]="'medium'"></sae-m-button>
<sae-m-button (click)="submitFeedback()" [kind]="'primary'" [label]="'Soumettre'"
[size]="'medium'"></sae-m-button>
</div>
<!-- <button class="close-button" (click)="toggleModal()">-->
<!-- <i class="ri-close-line"></i>-->
<!-- </button>-->
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -23,8 +51,8 @@
<textarea <textarea
[(ngModel)]="feedbackText" [(ngModel)]="feedbackText"
placeholder="Enter your feedback here..."
[disabled]="isSubmitting" [disabled]="isSubmitting"
placeholder="Enter your feedback here..."
rows="5" rows="5"
></textarea> ></textarea>
@ -42,27 +70,27 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<!-- <button--> <!-- <button-->
<!-- class="cancel-button"--> <!-- class="cancel-button"-->
<!-- (click)="toggleModal()"--> <!-- (click)="toggleModal()"-->
<!-- [disabled]="isSubmitting"--> <!-- [disabled]="isSubmitting"-->
<!-- >--> <!-- >-->
<!-- Cancel--> <!-- Cancel-->
<!-- </button>--> <!-- </button>-->
<!-- <button--> <!-- <button-->
<!-- class="submit-button"--> <!-- class="submit-button"-->
<!-- (click)="submitFeedback()"--> <!-- (click)="submitFeedback()"-->
<!-- [disabled]="isSubmitting || !feedbackText.trim()"--> <!-- [disabled]="isSubmitting || !feedbackText.trim()"-->
<!-- >--> <!-- >-->
<!-- --> <!-- -->
<!-- </button>--> <!-- </button>-->
@if (isSubmitting) { @if (isSubmitting) {
<i class="ri-loader-4-line spinning"></i> Sending... <i class="ri-loader-4-line spinning"></i> Sending...
} @else { } @else {
<!-- Send Feedback--> <!-- Send Feedback-->
} }
</div> </div>
</div> </div>
</div> </div>

View file

@ -3,10 +3,21 @@
:host { :host {
$feedback_left: 47px;
.side {
padding-top: 16px;
padding-right: 16px;
+ .side {
text-align: right
}
}
.feedback-button { .feedback-button {
font-family: variables.$font-family; font-family: variables.$font-family;
background: #E3EAF1; background: #E3EAF1;
color: #005AA2; color: variables.$main-color-400;
border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
transform: rotate(270deg); transform: rotate(270deg);
cursor: pointer; cursor: pointer;
@ -29,6 +40,11 @@
line-height: 8px; /* 57.143% */ line-height: 8px; /* 57.143% */
&.is-active {
right: 646px;
}
&:hover { &:hover {
background: #d9e8f6; background: #d9e8f6;
} }
@ -36,173 +52,224 @@
i { i {
font-size: 16px; font-size: 16px;
} }
}
// Modal styles // Modal styles
.feedback-modal-overlay { .feedback-modal-overlay {
position: fixed;
top: 92px;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.feedback-modal { position: fixed;
background: #E3EAF1; top: 0;
border-radius: 8px; left: 0;
width: 90vw; right: 0;
max-width: 500px; bottom: 0;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); background-color: rgba(0, 0, 0, 0.5);
display: flex; display: flex;
flex-direction: column; justify-content: end;
max-height: 90vh; align-items: start;
overflow: hidden; z-index: 1000;
} overflow: hidden;
width: 100vw;
.modal-header { height: 100vh;
background: transparent; padding-top: 200px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #eee;
h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #083b7d;
}
.close-button {
background: none;
border: none;
cursor: pointer;
font-size: 20px;
color: #666;
padding: 4px;
&:hover {
color: #333;
}
}
}
.modal-body {
padding: 20px;
overflow-y: auto;
background: transparent;
.modal-description {
margin-bottom: 16px;
color: #555;
}
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
font-family: inherit;
font-size: 14px;
&:focus {
outline: none;
border-color: #083b7d;
}
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
}
.success-message, .error-message {
margin-top: 16px;
padding: 10px;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
i {
font-size: 18px;
}
}
.success-message {
background-color: #e6f7e6;
color: #2e7d32;
}
.error-message {
background-color: #fdecea;
color: #d32f2f;
}
}
.modal-footer {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
button {
padding: 8px 16px;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.cancel-button {
background: none;
border: 1px solid #ddd;
color: #555;
&:hover:not(:disabled) {
background-color: #f5f5f5;
}
}
.submit-button {
background-color: #083b7d;
color: white;
border: none;
display: flex;
align-items: center;
gap: 8px;
&:hover:not(:disabled) {
background-color: #062c5e;
}
}
}
// Spinner animation
.spinning {
animation: spin 1s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
} }
.feedback-modal {
background: #E3EAF1;
border-radius: 8px;
width: 90vw;
max-width: 700px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
max-height: 90vh;
overflow: hidden;
.feedback-button {
background: transparent;
}
}
.modal-header {
background: transparent;
display: flex;
justify-content: space-between;
align-items: top;
padding: 13px 0;
border-bottom: 1px solid #eee;
padding-left: $feedback_left;
h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--Black-text, #1B1D27);
leading-trim: both;
text-edge: cap;
font-family: Barlow;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 0; /* 0% */
margin-bottom: 8px;
margin-top: 12px;
}
sae-m-button {
margin-left: 6px;
}
.close-button {
background: none;
border: none;
cursor: pointer;
font-size: 20px;
color: #666;
padding: 4px;
&:hover {
color: #333;
}
}
}
.modal-body {
padding: 0 20px;
overflow-y: auto;
background: transparent;
padding-left: $feedback_left;
.modal-description {
margin-bottom: 16px;
leading-trim: both;
text-edge: cap;
font-family: Barlow;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 0; /* 0% */
color: variables.$neutral-blue;
}
textarea {
margin-top: 16px;
width: 100%;
padding: 12px;
resize: vertical;
font-family: inherit;
font-size: 14px;
height: 140px;
border: solid 1px #a8acc2;
color: #000;
line-height: 140%; /* 19.6px */
border-radius: 8px;
background: #FFF;
&:focus {
outline: none;
border-color: #083b7d;
}
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
}
.success-message, .error-message {
margin-top: 16px;
padding: 10px;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
i {
font-size: 18px;
}
}
.success-message {
background-color: #e6f7e6;
color: #2e7d32;
}
.error-message {
background-color: #fdecea;
color: #d32f2f;
}
}
.modal-footer {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
button {
padding: 8px 16px;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.cancel-button {
background: none;
border: 1px solid #ddd;
color: #555;
&:hover:not(:disabled) {
background-color: #f5f5f5;
}
}
.submit-button {
background-color: #083b7d;
color: white;
border: none;
display: flex;
align-items: center;
gap: 8px;
&:hover:not(:disabled) {
background-color: #062c5e;
}
}
}
// Spinner animation
.spinning {
animation: spin 1s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.star-row {
padding-top: 9px;
i {
font-size: 20px;
}
}
} }

View file

@ -1,12 +1,14 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {MainButton} from '../main-button/main-button'; import {MainButton} from '../main-button/main-button';
import {NgClass} from '@angular/common';
@Component({ @Component({
selector: 'sae-feedback-button', selector: 'sae-feedback-button',
imports: [ imports: [
FormsModule, FormsModule,
MainButton MainButton,
NgClass
], ],
templateUrl: './feedback-button.html', templateUrl: './feedback-button.html',
styleUrl: './feedback-button.scss' styleUrl: './feedback-button.scss'
@ -14,11 +16,12 @@ import {MainButton} from '../main-button/main-button';
export class FeedbackButton { export class FeedbackButton {
isModalOpen: boolean = false; isModalOpen: boolean = false;
// isModalOpen: boolean = true;
feedbackText: string = ''; feedbackText: string = '';
isSubmitting: boolean = false; isSubmitting: boolean = false;
submitSuccess: boolean = false; submitSuccess: boolean = false;
submitError: boolean = false; submitError: boolean = false;
protected readonly close = close;
toggleModal() { toggleModal() {
this.isModalOpen = !this.isModalOpen; this.isModalOpen = !this.isModalOpen;
@ -30,14 +33,6 @@ export class FeedbackButton {
this.submitError = false; this.submitError = false;
} }
// Update app state to show/hide feedback panel
// this.store.dispatch({
// type: ActionTypes.UPDATE_APP,
// payload: {
// displayFeedBackPanel: this.isModalOpen,
// feedBackInput: this.feedbackText
// }
// });
} }
async submitFeedback() { async submitFeedback() {
@ -50,24 +45,6 @@ export class FeedbackButton {
this.submitError = false; this.submitError = false;
try { try {
// Update Redux state with feedback text
// this.store.dispatch({
// type: ActionTypes.UPDATE_APP,
// payload: {
// feedBackInput: this.feedbackText
// }
// });
//
// // Dispatch action to send feedback
// this.store.dispatch({
// type: ActionTypes.SEND_USER_FEEDBACK,
// payload: {
// feedback: this.feedbackText
// }
// });
//
// // Call API service directly
// await this.apiService.sendUserFeedback(this.feedbackText);
this.submitSuccess = true; this.submitSuccess = true;