data-primals-engine 1.0.10 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/client/README.md +8 -0
  2. package/client/index.html +31 -0
  3. package/client/index.js +25 -0
  4. package/client/package-lock.json +11664 -0
  5. package/client/package.json +70 -0
  6. package/client/public/demo/26899917-d1ba-4df4-bb33-48d09a8778da.jpg +0 -0
  7. package/client/public/demo/4b9894c7-12cd-466d-8fd3-a2757b09ec96.jpg +0 -0
  8. package/client/public/demo/7ed369ac-a1a2-4b45-b0cd-4fd5bcf5a75a.jpg +0 -0
  9. package/client/public/doc/API.postman_collection.json +214 -0
  10. package/client/public/github.svg +1 -0
  11. package/client/public/profilCompany.jpg +0 -0
  12. package/client/public/profilDeveloper.jpg +0 -0
  13. package/client/public/profilEngineer.jpg +0 -0
  14. package/client/public/profilPersonal.jpg +0 -0
  15. package/client/public/profils .psd +0 -0
  16. package/client/public/react.svg +9 -0
  17. package/client/src/APIInfo.jsx +328 -0
  18. package/client/src/AddWidgetTypeModal.jsx +44 -0
  19. package/client/src/AddWidgetTypeModal.scss +87 -0
  20. package/client/src/App.css +42 -0
  21. package/client/src/App.jsx +586 -0
  22. package/client/src/App.scss +1466 -0
  23. package/client/src/AssistantChat.jsx +280 -0
  24. package/client/src/AssistantChat.scss +229 -0
  25. package/client/src/Button.jsx +12 -0
  26. package/client/src/Button.scss +197 -0
  27. package/client/src/CalculationBuilder.jsx +338 -0
  28. package/client/src/CalculationStepRow.jsx +199 -0
  29. package/client/src/CalendarConfigModal.jsx +49 -0
  30. package/client/src/ChartConfigModal.jsx +402 -0
  31. package/client/src/ConditionBuilder.jsx +622 -0
  32. package/client/src/ConditionBuilder.scss +672 -0
  33. package/client/src/ConditionBuilder2.jsx +757 -0
  34. package/client/src/ContentView.jsx +274 -0
  35. package/client/src/CronBuilder.jsx +126 -0
  36. package/client/src/CronBuilder.scss +128 -0
  37. package/client/src/CronPartBuilder.jsx +150 -0
  38. package/client/src/Dashboard.jsx +210 -0
  39. package/client/src/Dashboard.scss +445 -0
  40. package/client/src/DashboardChart.jsx +362 -0
  41. package/client/src/DashboardFlexViewItem.jsx +129 -0
  42. package/client/src/DashboardView.jsx +554 -0
  43. package/client/src/DataChart.jsx +349 -0
  44. package/client/src/DataEditor.jsx +556 -0
  45. package/client/src/DataLayout.jsx +709 -0
  46. package/client/src/DataTable.jsx +996 -0
  47. package/client/src/DataTable.scss +73 -0
  48. package/client/src/Dialog.jsx +90 -0
  49. package/client/src/Dialog.scss +117 -0
  50. package/client/src/DisplayFlexNodeRenderer.jsx +115 -0
  51. package/client/src/DocumentationPageLayout.jsx +93 -0
  52. package/client/src/DocumentationPageLayout.scss +262 -0
  53. package/client/src/Draggable.jsx +222 -0
  54. package/client/src/Draggable.scss +49 -0
  55. package/client/src/ExportDialog.jsx +172 -0
  56. package/client/src/Field.jsx +1558 -0
  57. package/client/src/FlexBuilder.jsx +269 -0
  58. package/client/src/FlexBuilder.scss +226 -0
  59. package/client/src/FlexBuilderControls.jsx +170 -0
  60. package/client/src/FlexBuilderModal.jsx +75 -0
  61. package/client/src/FlexBuilderModal.scss +109 -0
  62. package/client/src/FlexBuilderPreview.jsx +48 -0
  63. package/client/src/FlexBuilderUtils.js +84 -0
  64. package/client/src/FlexDataRenderer.jsx +170 -0
  65. package/client/src/FlexDataRenderer.scss +80 -0
  66. package/client/src/FlexNode.jsx +290 -0
  67. package/client/src/FlexTreeUtils.js +129 -0
  68. package/client/src/HiddenableCell.jsx +46 -0
  69. package/client/src/HiddenableCell.scss +36 -0
  70. package/client/src/KPIDialog.jsx +39 -0
  71. package/client/src/KPIWidget.jsx +139 -0
  72. package/client/src/KanbanCard.jsx +92 -0
  73. package/client/src/KanbanColumn.jsx +29 -0
  74. package/client/src/KanbanConfigModal.jsx +115 -0
  75. package/client/src/KanbanView.jsx +113 -0
  76. package/client/src/KanbanView.scss +107 -0
  77. package/client/src/MessageRotator.jsx +77 -0
  78. package/client/src/MessageRotator.scss +14 -0
  79. package/client/src/ModelCreator.jsx +640 -0
  80. package/client/src/ModelCreator.scss +154 -0
  81. package/client/src/ModelCreatorField.jsx +810 -0
  82. package/client/src/ModelImporter.jsx +120 -0
  83. package/client/src/ModelList.jsx +205 -0
  84. package/client/src/Notification.jsx +137 -0
  85. package/client/src/Notification.scss +126 -0
  86. package/client/src/NotificationProvider.jsx +73 -0
  87. package/client/src/PackGallery.jsx +211 -0
  88. package/client/src/PackGallery.scss +156 -0
  89. package/client/src/Pagination.jsx +141 -0
  90. package/client/src/RTE.jsx +415 -0
  91. package/client/src/RTETrans.jsx +103 -0
  92. package/client/src/RelationField.jsx +247 -0
  93. package/client/src/RelationValue.jsx +230 -0
  94. package/client/src/RestoreConfirmationModal.jsx +183 -0
  95. package/client/src/RestoreDialog.jsx +134 -0
  96. package/client/src/RestoreDialog.scss +67 -0
  97. package/client/src/RichTextEditorModal.jsx +34 -0
  98. package/client/src/RichTextEditorModal.scss +54 -0
  99. package/client/src/TourSpotlight.jsx +234 -0
  100. package/client/src/TourSpotlight.scss +100 -0
  101. package/client/src/TutorialsMenu.jsx +83 -0
  102. package/client/src/TutorialsMenu.scss +217 -0
  103. package/client/src/TutorialsRewardModal.jsx +65 -0
  104. package/client/src/Utils.jsx +78 -0
  105. package/client/src/ViewSwitcher.jsx +54 -0
  106. package/client/src/ViewSwitcher.scss +74 -0
  107. package/client/src/Webpage.jsx +69 -0
  108. package/client/src/_variables.scss +21 -0
  109. package/client/src/assets/react.svg +1 -0
  110. package/client/src/constants.js +68 -0
  111. package/client/src/contexts/AuthContext.jsx +28 -0
  112. package/client/src/contexts/ModelContext.jsx +328 -0
  113. package/client/src/contexts/UIContext.jsx +52 -0
  114. package/client/src/core/data.js +35 -0
  115. package/client/src/entry-client.jsx +15 -0
  116. package/client/src/entry-server.jsx +13 -0
  117. package/client/src/filter.js +222 -0
  118. package/client/src/hooks/data.js +101 -0
  119. package/client/src/hooks/useDebounce.js +20 -0
  120. package/client/src/hooks/useDragAndDrop.js +59 -0
  121. package/client/src/hooks/useLocalStorage.js +100 -0
  122. package/client/src/hooks/useMount.js +42 -0
  123. package/client/src/hooks/useTutorials.jsx +227 -0
  124. package/client/src/hooks/useWindowSize.js +24 -0
  125. package/client/src/i18n.js +47 -0
  126. package/client/src/index.css +90 -0
  127. package/client/src/index.js +10 -0
  128. package/client/src/main.jsx +11 -0
  129. package/client/src/translations.js +15940 -0
  130. package/client/src/tutorials.js +112 -0
  131. package/client/vite.config.js +30 -0
  132. package/eslint.config.js +50 -0
  133. package/package.json +48 -27
  134. package/server.js +9 -0
  135. package/src/constants.js +12 -23
  136. package/src/core.js +9 -8
  137. package/src/data.js +81 -80
  138. package/src/defaultModels.js +114 -115
  139. package/src/email.js +8 -9
  140. package/src/engine.js +5 -5
  141. package/src/events.js +1 -2
  142. package/src/i18n.js +5390 -211
  143. package/src/middlewares/middleware-mongodb.js +94 -93
  144. package/src/migrate.js +24 -24
  145. package/src/modules/assistant.js +55 -55
  146. package/src/modules/bucket.js +10 -9
  147. package/src/modules/data.js +387 -357
  148. package/src/modules/file.js +3 -6
  149. package/src/modules/mongodb.js +6 -7
  150. package/src/modules/user.js +24 -47
  151. package/src/modules/workflow.js +68 -67
  152. package/src/openai.jobs.js +2 -2
  153. package/src/packs.js +30 -31
  154. package/src/providers.js +101 -3
  155. package/src/setenv.js +1 -1
  156. package/src/user.js +0 -17
  157. package/src/workers/crypto-worker.js +21 -20
  158. package/src/workers/import-export-worker.js +18 -18
  159. package/test/data.backup.integration.test.js +7 -12
  160. package/test/data.integration.test.js +27 -25
  161. package/test/globalSetup.js +2 -2
  162. package/test/import_export.integration.test.js +6 -6
  163. package/test/model.integration.test.js +21 -22
  164. package/test/workflow.integration.test.js +2 -5
  165. package/test/workflow.robustness.test.js +6 -13
  166. package/vitest.config.js +2 -2
@@ -0,0 +1,280 @@
1
+ // client/src/components/AssistantChat.jsx
2
+
3
+ import React, { useState, useMemo, useEffect, useRef } from 'react';
4
+ import { FaRobot, FaPaperPlane, FaTimes, FaExpand, FaCompress } from 'react-icons/fa';
5
+ import './AssistantChat.scss';
6
+ import { useModelContext } from "./contexts/ModelContext.jsx";
7
+ import { Trans, useTranslation } from "react-i18next";
8
+ import Markdown from 'react-markdown';
9
+ import {useQueryClient} from "react-query";
10
+
11
+ const AssistantChat = ({ config }) => {
12
+ const { selectedModel } = useModelContext();
13
+ const { t } = useTranslation();
14
+ const [isOpen, setIsOpen] = useState(false);
15
+ const [isMaximized, setIsMaximized] = useState(false);
16
+ const [messages, setMessages] = useState([
17
+ { from: 'bot', text: t('assistant.welcome') }
18
+ ]);
19
+ const [input, setInput] = useState('');
20
+ const [isLoading, setIsLoading] = useState(false);
21
+
22
+ // NOUVEL ÉTAT : Stocke une action en attente de confirmation de l'utilisateur
23
+ const [pendingConfirmation, setPendingConfirmation] = useState(null);
24
+
25
+ // NOUVEAU : Référence pour le défilement automatique
26
+ const messagesEndRef = useRef(null);
27
+
28
+ // Fonction pour défiler vers le bas
29
+ const scrollToBottom = () => {
30
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
31
+ };
32
+
33
+ const queryClient = useQueryClient();
34
+
35
+ // Défiler vers le bas à chaque nouveau message ou changement de statut de chargement
36
+ useEffect(scrollToBottom, [messages, isLoading]);
37
+
38
+ // Fonction centralisée pour appeler l'API de l'assistant
39
+ const handleApiCall = async (payload) => {
40
+ setIsLoading(true);
41
+ // On efface toute confirmation en attente dès qu'une nouvelle action est lancée
42
+ setPendingConfirmation(null);
43
+ const isConfirmation = !!payload.confirmedAction;
44
+ if (!isConfirmation) {
45
+ setPendingConfirmation(null);
46
+ }
47
+ try {
48
+ const response = await fetch('/api/assistant/chat', {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify(payload)
52
+ });
53
+
54
+ if (!response.ok) {
55
+ throw new Error(`Erreur HTTP: ${response.status}`);
56
+ }
57
+
58
+ const result = await response.json();
59
+
60
+ if (result.success) {
61
+ if (isConfirmation) {
62
+ const modelToInvalidate = payload.confirmedAction.params.model;
63
+ if (modelToInvalidate) {
64
+ console.log(`[Assistant] Action on model '${modelToInvalidate}' succeeded. Invalidating cache.`);
65
+ // Invalide toutes les requêtes qui commencent par ce tableau.
66
+ // C'est la mode la plus simple et la plus sûre pour s'assurer
67
+ // que toutes les vues de ces données (paginées ou non) sont rafraîchies.
68
+ queryClient.invalidateQueries(['api/data', modelToInvalidate]);
69
+ }
70
+ }
71
+
72
+ // On crée un objet de message pour le bot qui peut contenir plus que du texte
73
+ const botMessage = {
74
+ from: 'bot',
75
+ text: null,
76
+ actionDetails: null // Pour stocker les détails de l'action à afficher
77
+ };
78
+
79
+ // Gérer le texte à afficher
80
+ if (result.displayMessage) {
81
+ botMessage.text = result.displayMessage;
82
+ } else if (result.codeMessage) {
83
+ const code = typeof result.codeMessage === 'object'
84
+ ? JSON.stringify(result.codeMessage, null, 2)
85
+ : result.codeMessage;
86
+ botMessage.text = `\`\`\`json\n${code}\n\`\`\``;
87
+ }
88
+
89
+ // NOUVEAU : Si une confirmation est demandée, on enrichit le message
90
+ if (result.confirmationRequest) {
91
+ // On stocke l'action complète dans l'état pour l'envoyer si l'utilisateur confirme
92
+ setPendingConfirmation(result.confirmationRequest);
93
+
94
+ // On ajoute les détails (modèle, filtre, données) au message pour l'affichage
95
+ botMessage.actionDetails = {
96
+ model: result.model,
97
+ filter: result.filter,
98
+ data: result.data // Sera undefined si non présent, ce qui est correct
99
+ };
100
+ } else if (isConfirmation) {
101
+ // Si c'était un appel de confirmation et qu'il n'y a pas de nouvelle demande, on vide l'état
102
+ setPendingConfirmation(null);
103
+ }
104
+
105
+ // On ajoute le message à la liste uniquement s'il a du contenu textuel
106
+ if (botMessage.text) {
107
+ setMessages(prev => [...prev, botMessage]);
108
+ }
109
+
110
+ } else {
111
+ const errorMessage = { from: 'bot', text: t('assistant.error', `Désolé, une erreur est survenue : {{message}}`, { message: result.message }) };
112
+ setMessages(prev => [...prev, errorMessage]);
113
+ }
114
+
115
+ } catch (error) {
116
+ const errorMessage = { from: 'bot', text: t('assistant.contactError', `Désolé, impossible de contacter l'assistant. ({{message}})`, { message: error.message }) };
117
+ setMessages(prev => [...prev, errorMessage]);
118
+ } finally {
119
+ setIsLoading(false);
120
+ }
121
+ };
122
+
123
+ // Gère la soumission du formulaire de chat
124
+ const handleSubmit = async (e) => {
125
+ e.preventDefault();
126
+ // On ne soumet rien si le champ est vide, si une requête est en cours ou si une confirmation est attendue
127
+ if (!input.trim() || isLoading || pendingConfirmation) return;
128
+
129
+ const userMessage = { from: 'user', text: input };
130
+ const currentInput = input;
131
+
132
+ setMessages(prev => [...prev, userMessage]);
133
+ setInput('');
134
+
135
+ gtag('event', "Assistant Prior - msg");
136
+
137
+ await handleApiCall({
138
+ message: currentInput,
139
+ history: messages,
140
+ provider: selectedProvider,
141
+ context: { modelName: selectedModel?.name }
142
+ });
143
+ };
144
+
145
+ // NOUVELLE FONCTION : Gère la réponse de l'utilisateur à une demande de confirmation
146
+ const handleConfirmAction = async (isConfirmed) => {
147
+ if (!pendingConfirmation) return;
148
+
149
+ if (isConfirmed) {
150
+ // L'utilisateur a cliqué sur "Oui"
151
+ const confirmationMessage = { from: 'user', text: t('yes', 'Oui') };
152
+ setMessages(prev => [...prev, confirmationMessage]);
153
+
154
+ // On rappelle l'API en envoyant l'action à confirmer
155
+ await handleApiCall({
156
+ message: "Action confirmée par l'utilisateur.",
157
+ history: messages,
158
+ provider: selectedProvider,
159
+ context: { modelName: selectedModel?.name },
160
+ confirmedAction: pendingConfirmation
161
+ });
162
+ } else {
163
+ // L'utilisateur a cliqué sur "Non"
164
+ const cancelMessage = { from: 'user', text: t('no', 'Non') };
165
+ const botResponseMessage = { from: 'bot', text: t('assistant.actionCancelled', "Action annulée.") };
166
+ setMessages(prev => [...prev, cancelMessage, botResponseMessage]);
167
+ setPendingConfirmation(null); // On annule la demande de confirmation
168
+ }
169
+ };
170
+
171
+ // Logique pour le sélecteur de fournisseur (OpenAI/Google)
172
+ const availableProviders = useMemo(() => {
173
+ const providers = [];
174
+ if (config?.openai) providers.push({ value: 'openai', label: 'OpenAI' });
175
+ if (config?.google) providers.push({ value: 'google', label: 'Google' });
176
+ return providers;
177
+ }, [config]);
178
+
179
+ const [selectedProvider, setSelectedProvider] = useState(null);
180
+
181
+ useEffect(() => {
182
+ if (availableProviders.length > 0 && !selectedProvider) {
183
+ setSelectedProvider(availableProviders[0].value);
184
+ }
185
+ }, [availableProviders, selectedProvider]);
186
+
187
+ // Si le chat est fermé, on affiche juste le bouton flottant
188
+ if (!isOpen) {
189
+ return (
190
+ <button className="assistant-fab" onClick={() => setIsOpen(true)} title={t('assistant.open', "Ouvrir l'assistant Prior")}>
191
+ <FaRobot />
192
+ </button>
193
+ );
194
+ }
195
+
196
+ // Si le chat est ouvert, on affiche la fenêtre complète
197
+ return (
198
+ <div className={`assistant-chat-window ${isMaximized ? 'maximized' : ''}`}>
199
+ <div className="chat-header">
200
+ <h3><FaRobot style={{ marginRight: '8px' }} /> <Trans i18nKey="assistant.named" values={{ named: 'Prior' }} /></h3>
201
+ <div className="header-actions">
202
+ {availableProviders.length > 1 && (
203
+ <select
204
+ className="provider-selector"
205
+ value={selectedProvider}
206
+ onChange={(e) => setSelectedProvider(e.target.value)}
207
+ >
208
+ {availableProviders.map(provider => (
209
+ <option key={provider.value} value={provider.value}>
210
+ {provider.label}
211
+ </option>
212
+ ))}
213
+ </select>
214
+ )}
215
+ <button onClick={() => setIsMaximized(!isMaximized)} title={isMaximized ? t('collapse', "Réduire") : t('expand', "Agrandir")}>
216
+ {isMaximized ? <FaCompress /> : <FaExpand />}
217
+ </button>
218
+ <button onClick={() => setIsOpen(false)} title={t('close', "Fermer")}>
219
+ <FaTimes />
220
+ </button>
221
+ </div>
222
+ </div>
223
+
224
+ <div className="chat-messages">
225
+ {messages.map((msg, index) => (
226
+ <div key={index} className={`message ${msg.from}`}>
227
+ <Markdown>{msg.text}</Markdown>
228
+ {msg.actionDetails && (
229
+ <div className="action-details">
230
+ {msg.actionDetails.model && (
231
+ <p><strong>{t('model', 'Modèle')}:</strong> <code>{msg.actionDetails.model}</code></p>
232
+ )}
233
+ {msg.actionDetails.filter && (
234
+ <>
235
+ <p><strong>{t('filter', 'Filtre')}:</strong></p>
236
+ {/* Utiliser Markdown pour afficher un bloc de code JSON formaté */}
237
+ <Markdown>{`\`\`\`json\n${JSON.stringify(msg.actionDetails.filter, null, 2)}\n\`\`\``}</Markdown>
238
+ </>
239
+ )}
240
+ {msg.actionDetails.data && (
241
+ <>
242
+ <p><strong>{t('data', 'Données')}:</strong></p>
243
+ <Markdown>{`\`\`\`json\n${JSON.stringify(msg.actionDetails.data, null, 2)}\n\`\`\``}</Markdown>
244
+ </>
245
+ )}
246
+ </div>
247
+ )}
248
+ </div>
249
+ ))}
250
+ {isLoading && <div className="message bot"><p>...</p></div>}
251
+
252
+ {/* NOUVEAU : Affichage des boutons de confirmation */}
253
+ {pendingConfirmation && !isLoading && (
254
+ <div className="message bot confirmation-prompt">
255
+ <button onClick={() => handleConfirmAction(true)}>{t('yes', 'Oui')}</button>
256
+ <button onClick={() => handleConfirmAction(false)} className="cancel">{t('no', 'Non')}</button>
257
+ </div>
258
+ )}
259
+ <div ref={messagesEndRef} />
260
+ </div>
261
+
262
+ <form className="chat-input-form" onSubmit={handleSubmit}>
263
+ <input
264
+ type="text"
265
+ value={input}
266
+ onChange={(e) => setInput(e.target.value)}
267
+ placeholder={t('assistant.type', "Écrivez votre message...")}
268
+ // On désactive l'input si une requête est en cours ou si une confirmation est attendue
269
+ disabled={isLoading || !!pendingConfirmation}
270
+ autoFocus
271
+ />
272
+ <button type="submit" disabled={isLoading || !input.trim() || !!pendingConfirmation} title={t('send', "Envoyer")}>
273
+ <FaPaperPlane />
274
+ </button>
275
+ </form>
276
+ </div>
277
+ );
278
+ };
279
+
280
+ export default AssistantChat;
@@ -0,0 +1,229 @@
1
+ // client/src/components/AssistantChat.scss
2
+
3
+ // Variables pour une maintenance facile
4
+ $primary-color: #007bff;
5
+ $bot-message-bg: #f1f0f0;
6
+ $user-message-bg: #e6f2ff;
7
+ $chat-window-bg: #ffffff;
8
+ $header-height: 50px;
9
+ $input-height: 50px;
10
+
11
+ // Le bouton flottant (quand le chat est fermé)
12
+ .assistant-fab {
13
+ position: fixed;
14
+ bottom: 20px;
15
+ right: 20px;
16
+ width: 60px;
17
+ height: 60px;
18
+ background-color: $primary-color;
19
+ color: white;
20
+ border-radius: 50%;
21
+ border: none;
22
+ display: flex;
23
+ align-items: center;
24
+ justify-content: center;
25
+ font-size: 24px;
26
+ cursor: pointer;
27
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
28
+ z-index: 1000;
29
+ transition: transform 0.2s ease-in-out;
30
+
31
+ &:hover {
32
+ transform: scale(1.1);
33
+ }
34
+ }
35
+
36
+ // La fenêtre de chat (quand elle est ouverte)
37
+ .assistant-chat-window {
38
+ position: fixed;
39
+ bottom: 20px;
40
+ right: 20px;
41
+ width: 100%;
42
+ left: 2px;
43
+ max-width: 720px;
44
+ min-height: 400px;
45
+ height: 60vh;
46
+ max-height: calc(100vh - 40px);
47
+ background: $chat-window-bg;
48
+ border-radius: 12px;
49
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.25);
50
+ display: flex;
51
+ flex-direction: column;
52
+ overflow: hidden;
53
+ z-index: 1001;
54
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
55
+
56
+ // Style pour la vue maximisée
57
+ &.maximized {
58
+ top: 0;
59
+ left: 0;
60
+ right: 0;
61
+ bottom: 0;
62
+ width: 100%;
63
+ height: 100%;
64
+ border-radius: 0;
65
+ max-height: 100vh;
66
+ z-index: 10000000;
67
+ }
68
+ }
69
+
70
+ // En-tête du chat
71
+ .chat-header {
72
+ height: $header-height;
73
+ background-color: #f7f7f7;
74
+ border-bottom: 1px solid #e0e0e0;
75
+ display: flex;
76
+ align-items: center;
77
+ justify-content: space-between;
78
+ padding: 0 15px;
79
+ flex-shrink: 0;
80
+
81
+ h3 {
82
+ font-size: 16px;
83
+ font-weight: 600;
84
+ display: flex;
85
+ align-items: center;
86
+ }
87
+
88
+ .header-actions {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 5px;
92
+
93
+ // NOUVEAU : Style pour le sélecteur
94
+ .provider-selector {
95
+ background-color: #e9ecef;
96
+ border: 1px solid #ced4da;
97
+ border-radius: 4px;
98
+ padding: 2px 6px;
99
+ font-size: 12px;
100
+ margin-right: 5px;
101
+ cursor: pointer;
102
+ }
103
+
104
+ button {
105
+ background: none;
106
+ border: none;
107
+ cursor: pointer;
108
+ font-size: 16px;
109
+ color: #666;
110
+ padding: 5px;
111
+ border-radius: 50%;
112
+ &:hover {
113
+ background-color: #e0e0e0;
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // Zone des messages
120
+ .chat-messages {
121
+ flex-grow: 1;
122
+ padding: 15px;
123
+ overflow-y: auto;
124
+ display: flex;
125
+ flex-direction: column;
126
+ gap: 12px;
127
+
128
+ .message {
129
+ max-width: 80%;
130
+ p {
131
+ padding: 4px 8px;
132
+ border-radius: 18px;
133
+ margin: 0;
134
+ line-height: 1.5;
135
+ word-wrap: break-word;
136
+ }
137
+
138
+ .action-details {
139
+ border-left: 3px solid #4a90e2; // Une bordure pour délimiter
140
+ padding: 10px;
141
+ margin-top: 12px;
142
+ background-color: rgba(0, 0, 0, 0.03); // Un fond léger
143
+ border-radius: 0 8px 8px 0;
144
+ font-size: 0.9em;
145
+
146
+ p {
147
+ margin: 0 0 5px 0;
148
+ }
149
+
150
+ // Style pour le nom du modèle
151
+ code {
152
+ background-color: rgba(0, 0, 0, 0.08);
153
+ padding: 2px 5px;
154
+ border-radius: 4px;
155
+ }
156
+
157
+ // Style pour les blocs de code JSON (filtre et données)
158
+ pre {
159
+ white-space: pre-wrap;
160
+ word-wrap: break-word;
161
+ background-color: #f5f5f5 !important; // Assurez-vous que le fond est visible
162
+ padding: 10px;
163
+ border-radius: 4px;
164
+ }
165
+ }
166
+ &.bot {
167
+ align-self: flex-start;
168
+ p {
169
+ background-color: $bot-message-bg;
170
+ border-bottom-left-radius: 4px;
171
+ }
172
+ }
173
+
174
+ &.user {
175
+ align-self: flex-end;
176
+ p {
177
+ background-color: $user-message-bg;
178
+ color: #333;
179
+ border-bottom-right-radius: 4px;
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ // Formulaire de saisie
186
+ .chat-input-form {
187
+ height: $input-height;
188
+ display: flex;
189
+ border-top: 1px solid #e0e0e0;
190
+ padding: 5px;
191
+ flex-shrink: 0;
192
+
193
+ input {
194
+ flex-grow: 1;
195
+ border: none;
196
+ padding: 0 10px;
197
+ font-size: 14px;
198
+ &:focus {
199
+ outline: none;
200
+ }
201
+ }
202
+
203
+ button {
204
+ background-color: $primary-color;
205
+ color: white;
206
+ border: none;
207
+ border-radius: 8px;
208
+ width: 40px;
209
+ height: 40px;
210
+ cursor: pointer;
211
+ font-size: 16px;
212
+ transition: background-color 0.2s;
213
+
214
+ &:hover {
215
+ background-color: darken($primary-color, 10%);
216
+ }
217
+ &:disabled {
218
+ background-color: #a0cfff;
219
+ cursor: not-allowed;
220
+ }
221
+ }
222
+ }
223
+
224
+
225
+ .message{
226
+ pre {
227
+ white-space: break-spaces;
228
+ }
229
+ }
@@ -0,0 +1,12 @@
1
+ import "./Button.scss";
2
+
3
+ function Button({ children, disabled, className, ...rest }) {
4
+ const cn = `${className || ""} btn${disabled ? " btn-disabled" : ""}`;
5
+ return (
6
+ <button disabled={disabled} {...rest} className={cn}>
7
+ {children}
8
+ </button>
9
+ );
10
+ }
11
+
12
+ export default Button;