add ask-ia page
This commit is contained in:
parent
0b8c217a91
commit
1e2f833509
8 changed files with 495 additions and 3 deletions
|
|
@ -27,7 +27,8 @@ import {
|
|||
Calculator,
|
||||
ListTodo,
|
||||
ShieldCheck,
|
||||
PanelLeft
|
||||
PanelLeft,
|
||||
MessageSquare,
|
||||
} from 'lucide-react';
|
||||
|
||||
export const MODULE_GESTION = 'gestion';
|
||||
|
|
@ -113,6 +114,12 @@ export const getMenuForModule = (currentModule) => {
|
|||
{ to: "/home/sage-builder", icon: PanelLeft, label: "Tableau des ventes" },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Modules",
|
||||
items: [
|
||||
{ to: "/home/ask-ia", icon: MessageSquare, label: "Sage Ask.AI" },
|
||||
]
|
||||
},
|
||||
// {
|
||||
// title: "Signature",
|
||||
// items: [
|
||||
|
|
|
|||
395
src/pages/AskIaPage.tsx
Normal file
395
src/pages/AskIaPage.tsx
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
import { ModalLoading } from '@/components/modal/ModalLoading';
|
||||
import { askIaStatus, getAskIaHtmlContent } from '@/store/features/ask-ia/selectors';
|
||||
import { getAskIa } from '@/store/features/ask-ia/thunk';
|
||||
import { sageBuilderError } from '@/store/features/sage-builder/selectors';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
const AskIaPage = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
|
||||
const htmlContent = useAppSelector(getAskIaHtmlContent);
|
||||
const status = useAppSelector(askIaStatus);
|
||||
const error = useAppSelector(sageBuilderError);
|
||||
|
||||
useEffect(() => {
|
||||
// Charger le dashboard au montage du composant
|
||||
if (status === 'idle') {
|
||||
dispatch(getAskIa());
|
||||
}
|
||||
}, [dispatch, status]);
|
||||
|
||||
useEffect(() => {
|
||||
// Injecter le HTML dans l'iframe quand il est disponible
|
||||
if (htmlContent && iframeRef.current && status === 'succeeded') {
|
||||
const iframeDoc = iframeRef.current.contentDocument || iframeRef.current.contentWindow?.document;
|
||||
if (iframeDoc) {
|
||||
iframeDoc.open();
|
||||
iframeDoc.write(htmlContent);
|
||||
iframeDoc.close();
|
||||
}
|
||||
}
|
||||
}, [htmlContent, status]);
|
||||
|
||||
const handleRetry = () => {
|
||||
dispatch(getAskIa());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full overflow-hidden bg-gray-50 relative">
|
||||
{/* Loading Overlay */}
|
||||
{status === 'loading' && <ModalLoading />}
|
||||
|
||||
{/* Error State */}
|
||||
{status === 'failed' && error && (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-gray-50">
|
||||
<div className="text-center max-w-md px-4">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 mb-4 bg-red-100 rounded-full">
|
||||
<svg
|
||||
className="w-8 h-8 text-red-600"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
Erreur de chargement
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
{error}
|
||||
</p>
|
||||
<button
|
||||
onClick={handleRetry}
|
||||
className="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
|
||||
>
|
||||
Réessayer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Iframe */}
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
className={`w-full h-full border-0 transition-opacity duration-300 ${
|
||||
status === 'succeeded' ? 'opacity-100' : 'opacity-0'
|
||||
}`}
|
||||
title="Sage Builder Dashboard"
|
||||
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AskIaPage;
|
||||
|
||||
|
||||
|
||||
|
||||
// import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
// import { Send, Loader2, ChevronDown, Plug, Zap, Cpu } from 'lucide-react';
|
||||
|
||||
// // Configuration (à adapter selon ton contexte)
|
||||
// const config = {
|
||||
// apiUrl: "https://api.sage-ai-studio.webexpr.dev",
|
||||
// authToken: "Bearer sk_live_bef70d4dd6bc671fd815d21104d5dce8",
|
||||
// fixedProduct: { slug: "sage100dataven", name: "Sage 100 (Dataven)" },
|
||||
// fixedModel: { key: "mistral-small", label: "Mistral Small" },
|
||||
// t: {
|
||||
// howCanIHelp: "Comment puis-je vous aider aujourd'hui ?",
|
||||
// querySageData: "Interrogez vos données Sage en langage naturel",
|
||||
// writeMessage: "Écrivez votre message...",
|
||||
// enterToSend: "Appuyez sur Entrée pour envoyer, Shift+Entrée pour une nouvelle ligne",
|
||||
// consulting: "Consultation en cours",
|
||||
// error: "Erreur"
|
||||
// }
|
||||
// };
|
||||
|
||||
// // Simple Markdown renderer
|
||||
// const renderMarkdown = (text) => {
|
||||
// if (!text) return '';
|
||||
// return text
|
||||
// .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
// .replace(/\*(.+?)\*/g, '<em>$1</em>')
|
||||
// .replace(/```([\s\S]*?)```/g, '<pre class="bg-gray-100 p-3 rounded-md overflow-x-auto my-2"><code>$1</code></pre>')
|
||||
// .replace(/`(.+?)`/g, '<code class="bg-gray-100 px-1.5 py-0.5 rounded text-sm">$1</code>')
|
||||
// .replace(/^### (.+)$/gm, '<h3 class="font-semibold text-base mt-3 mb-1">$1</h3>')
|
||||
// .replace(/^## (.+)$/gm, '<h2 class="font-semibold text-lg mt-3 mb-1">$1</h2>')
|
||||
// .replace(/^# (.+)$/gm, '<h1 class="font-bold text-xl mt-3 mb-2">$1</h1>')
|
||||
// .replace(/^- (.+)$/gm, '<li class="ml-4">• $1</li>')
|
||||
// .replace(/\n/g, '<br/>');
|
||||
// };
|
||||
|
||||
// // Message Component
|
||||
// const Message = ({ message, isUser }) => {
|
||||
// const modelLabel = message.model ? (
|
||||
// message.model.toLowerCase().includes('haiku') ? 'Haiku' :
|
||||
// message.model.toLowerCase().includes('sonnet') ? 'Sonnet' :
|
||||
// message.model.toLowerCase().includes('mistral-small') ? 'Mistral Small' :
|
||||
// message.model.toLowerCase().includes('mistral-large') ? 'Mistral Large' :
|
||||
// message.model
|
||||
// ) : null;
|
||||
|
||||
// return (
|
||||
// <div className={`flex gap-4 ${isUser ? 'justify-end' : 'justify-start'}`}>
|
||||
// {!isUser && (
|
||||
// <div className="flex size-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-r from-green-600 to-blue-600 text-white shadow-md">
|
||||
// <span className="font-bold text-sm">S</span>
|
||||
// </div>
|
||||
// )}
|
||||
// <div className={`max-w-[80%] rounded-2xl px-4 py-3 ${isUser ? 'bg-zinc-900 text-white' : 'bg-gray-100'}`}>
|
||||
// {isUser ? (
|
||||
// <p className="text-sm whitespace-pre-wrap">{message.content}</p>
|
||||
// ) : (
|
||||
// <>
|
||||
// <div
|
||||
// className="text-sm prose prose-sm max-w-none"
|
||||
// dangerouslySetInnerHTML={{ __html: renderMarkdown(message.content) }}
|
||||
// />
|
||||
// {modelLabel && (
|
||||
// <div className="flex items-center gap-1 mt-2 pt-2 border-t border-gray-200">
|
||||
// {message.model?.toLowerCase().includes('haiku') || message.model?.toLowerCase().includes('mistral-small') ? (
|
||||
// <Zap size={12} className="text-gray-500" />
|
||||
// ) : (
|
||||
// <Cpu size={12} className="text-gray-500" />
|
||||
// )}
|
||||
// <span className="text-xs text-gray-500">{modelLabel}</span>
|
||||
// </div>
|
||||
// )}
|
||||
// </>
|
||||
// )}
|
||||
// </div>
|
||||
// {isUser && (
|
||||
// <div className="flex size-8 shrink-0 items-center justify-center rounded-full bg-zinc-400 text-white">
|
||||
// <span className="font-medium text-sm">U</span>
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// // Loading Indicator
|
||||
// const LoadingIndicator = ({ tool }) => (
|
||||
// <div className="flex gap-4 justify-start">
|
||||
// <div className="flex size-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-r from-green-600 to-blue-600 text-white shadow-md">
|
||||
// <span className="font-bold text-sm">S</span>
|
||||
// </div>
|
||||
// {tool ? (
|
||||
// <div className="bg-amber-50 border border-amber-200 rounded-xl px-4 py-3">
|
||||
// <div className="flex items-center gap-2">
|
||||
// <Loader2 size={16} className="animate-spin text-amber-600" />
|
||||
// <span className="text-sm text-amber-600">
|
||||
// {config.t.consulting}: {tool}
|
||||
// </span>
|
||||
// </div>
|
||||
// </div>
|
||||
// ) : (
|
||||
// <div className="max-w-[80%] rounded-2xl bg-gray-100 px-4 py-3">
|
||||
// <div className="flex gap-1">
|
||||
// <div className="size-2 animate-bounce rounded-full bg-gray-400" style={{ animationDelay: '-0.3s' }} />
|
||||
// <div className="size-2 animate-bounce rounded-full bg-gray-400" style={{ animationDelay: '-0.15s' }} />
|
||||
// <div className="size-2 animate-bounce rounded-full bg-gray-400" />
|
||||
// </div>
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// );
|
||||
|
||||
// // Empty State
|
||||
// const EmptyState = () => (
|
||||
// <div className="h-full flex flex-col items-center justify-center gap-6 px-4 py-8">
|
||||
// <div className="flex size-16 items-center justify-center rounded-full bg-gradient-to-r from-green-600 to-blue-600 text-white shadow-lg">
|
||||
// <span className="font-bold text-3xl">S</span>
|
||||
// </div>
|
||||
// <div className="text-center">
|
||||
// <h2 className="mb-2 font-bold text-2xl text-gray-900">{config.t.howCanIHelp}</h2>
|
||||
// <p className="text-gray-500">{config.t.querySageData}</p>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
|
||||
// // Main Component
|
||||
// const AskIaPage = () => {
|
||||
// const [messages, setMessages] = useState([]);
|
||||
// const [input, setInput] = useState('');
|
||||
// const [isLoading, setIsLoading] = useState(false);
|
||||
// const [currentTool, setCurrentTool] = useState(null);
|
||||
// const [error, setError] = useState(null);
|
||||
// const scrollRef = useRef(null);
|
||||
// const inputRef = useRef(null);
|
||||
|
||||
// // Auto-scroll
|
||||
// useEffect(() => {
|
||||
// if (scrollRef.current) {
|
||||
// scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
// }
|
||||
// }, [messages, isLoading]);
|
||||
|
||||
// // Send message with streaming
|
||||
// const sendMessage = useCallback(async (text) => {
|
||||
// if (!text.trim() || isLoading) return;
|
||||
|
||||
// const userMessage = {
|
||||
// id: Date.now().toString(),
|
||||
// role: 'user',
|
||||
// content: text.trim(),
|
||||
// };
|
||||
|
||||
// setMessages(prev => [...prev, userMessage]);
|
||||
// setInput('');
|
||||
// setIsLoading(true);
|
||||
// setError(null);
|
||||
// setCurrentTool(null);
|
||||
|
||||
// const assistantId = (Date.now() + 1).toString();
|
||||
// setMessages(prev => [...prev, { id: assistantId, role: 'assistant', content: '', model: null }]);
|
||||
|
||||
// try {
|
||||
// const history = messages.map(m => ({ role: m.role, content: m.content }));
|
||||
|
||||
// const response = await fetch(config.apiUrl + '/api/ask/chat/stream', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'Authorization': config.authToken,
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// question: text.trim(),
|
||||
// history,
|
||||
// productSlug: config.fixedProduct.slug,
|
||||
// model: config.fixedModel.key,
|
||||
// }),
|
||||
// });
|
||||
|
||||
// if (!response.ok) {
|
||||
// const errorData = await response.json().catch(() => ({}));
|
||||
// throw new Error(errorData.error || 'Erreur ' + response.status);
|
||||
// }
|
||||
|
||||
// const reader = response.body.getReader();
|
||||
// const decoder = new TextDecoder();
|
||||
// let buffer = '';
|
||||
|
||||
// while (true) {
|
||||
// const { done, value } = await reader.read();
|
||||
// if (done) break;
|
||||
|
||||
// buffer += decoder.decode(value, { stream: true });
|
||||
// const lines = buffer.split('\n');
|
||||
// buffer = lines.pop() || '';
|
||||
|
||||
// for (const line of lines) {
|
||||
// if (line.startsWith('data: ')) {
|
||||
// try {
|
||||
// const data = JSON.parse(line.slice(6));
|
||||
// if (data.type === 'text') {
|
||||
// setMessages(prev => prev.map(m =>
|
||||
// m.id === assistantId ? { ...m, content: m.content + data.content } : m
|
||||
// ));
|
||||
// } else if (data.type === 'tool_start') {
|
||||
// setCurrentTool(data.tool);
|
||||
// } else if (data.type === 'tool_end') {
|
||||
// setCurrentTool(null);
|
||||
// } else if (data.type === 'complete') {
|
||||
// setMessages(prev => prev.map(m =>
|
||||
// m.id === assistantId ? { ...m, model: data.usage?.model || null } : m
|
||||
// ));
|
||||
// } else if (data.type === 'error') {
|
||||
// setError(data.message);
|
||||
// }
|
||||
// } catch (e) {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (err) {
|
||||
// setError(err.message || "Erreur lors de l'envoi du message");
|
||||
// setMessages(prev => prev.filter(m => m.id !== assistantId));
|
||||
// } finally {
|
||||
// setIsLoading(false);
|
||||
// setCurrentTool(null);
|
||||
// }
|
||||
// }, [messages, isLoading]);
|
||||
|
||||
// const handleKeyDown = (e) => {
|
||||
// if (e.key === 'Enter' && !e.shiftKey) {
|
||||
// e.preventDefault();
|
||||
// sendMessage(input);
|
||||
// }
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="flex flex-col h-screen bg-gray-50">
|
||||
// {/* Error alert */}
|
||||
// {error && (
|
||||
// <div className="shrink-0 px-6 pt-4">
|
||||
// <div className="p-4 rounded-lg bg-red-50 border border-red-200 text-red-700 text-sm">
|
||||
// <strong>{config.t.error}:</strong> {error}
|
||||
// </div>
|
||||
// </div>
|
||||
// )}
|
||||
|
||||
// {/* Messages area */}
|
||||
// <div ref={scrollRef} className="flex-1 overflow-y-auto">
|
||||
// {messages.length === 0 && !isLoading ? (
|
||||
// <EmptyState />
|
||||
// ) : (
|
||||
// <div className="mx-auto max-w-3xl space-y-6 px-4 py-6">
|
||||
// {messages.map(message => (
|
||||
// <Message key={message.id} message={message} isUser={message.role === 'user'} />
|
||||
// ))}
|
||||
// {isLoading && <LoadingIndicator tool={currentTool} />}
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
|
||||
// {/* Input area */}
|
||||
// <div className="shrink-0 border-t border-gray-200 bg-white p-4 px-6">
|
||||
// <div className="mx-auto max-w-3xl">
|
||||
// {/* Product badge */}
|
||||
// <div className="flex items-center gap-2 mb-3">
|
||||
// <div className="flex items-center gap-1.5 h-9 px-3 rounded-md border border-zinc-300 bg-zinc-50 text-sm">
|
||||
// <Plug size={14} className="text-zinc-600" />
|
||||
// <span className="font-medium text-zinc-700">{config.fixedProduct.name}</span>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// {/* Input + Send button */}
|
||||
// <div className="flex items-end gap-2">
|
||||
// <div className="flex-1">
|
||||
// <textarea
|
||||
// ref={inputRef}
|
||||
// value={input}
|
||||
// onChange={(e) => setInput(e.target.value)}
|
||||
// onKeyDown={handleKeyDown}
|
||||
// placeholder={config.t.writeMessage}
|
||||
// disabled={isLoading}
|
||||
// rows={1}
|
||||
// className="w-full resize-none rounded-lg border border-gray-300 bg-white px-4 py-3 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-zinc-900 focus:border-transparent disabled:opacity-50"
|
||||
// style={{ minHeight: '48px', maxHeight: '200px' }}
|
||||
// />
|
||||
// </div>
|
||||
// <button
|
||||
// onClick={() => sendMessage(input)}
|
||||
// disabled={isLoading || !input.trim()}
|
||||
// className="flex h-12 w-12 items-center justify-center rounded-lg bg-zinc-900 text-white transition-colors hover:bg-zinc-800 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
// >
|
||||
// {isLoading ? <Loader2 size={20} className="animate-spin" /> : <Send size={20} />}
|
||||
// </button>
|
||||
// </div>
|
||||
|
||||
// <p className="mt-2 text-center text-gray-400 text-xs">{config.t.enterToSend}</p>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default AskIaPage;
|
||||
|
|
@ -75,6 +75,7 @@ import InvoiceCreatePage from '@/pages/sales/InvoiceCreatePage';
|
|||
import SageBuilderPage from '@/pages/SageBuilderPage';
|
||||
import PaymentsPage from '@/pages/sales/PaymentsPage';
|
||||
import PaymentDetailPage from '@/pages/sales/PaymentDetailPage';
|
||||
import AskIaPage from '@/pages/AskIaPage';
|
||||
|
||||
const DatavenRoute = () => {
|
||||
return (
|
||||
|
|
@ -110,7 +111,10 @@ const DatavenRoute = () => {
|
|||
|
||||
{/* iframe */}
|
||||
<Route path="/sage-builder" element={<SageBuilderPage />} />
|
||||
|
||||
|
||||
{/* ask ia */}
|
||||
<Route path="/ask-ia" element={<AskIaPage />} />
|
||||
|
||||
{/* Sales */}
|
||||
<Route path="/opportunites" element={<OpportunitiesPipelinePage />} />
|
||||
<Route path="/opportunites/:id" element={<OpportunityDetailPage />} />
|
||||
|
|
|
|||
6
src/store/features/ask-ia/selectors.ts
Normal file
6
src/store/features/ask-ia/selectors.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
import type { RootState } from "@/store/store";
|
||||
|
||||
export const getAskIaHtmlContent = (state: RootState) => state.askIa.htmlContent;
|
||||
export const askIaStatus = (state: RootState) => state.askIa.status;
|
||||
export const askIaError = (state: RootState) => state.askIa.error;
|
||||
45
src/store/features/ask-ia/slice.ts
Normal file
45
src/store/features/ask-ia/slice.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// store/ask-ia/slice.ts
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { AskiaState } from "./type";
|
||||
import { getAskIa } from "./thunk";
|
||||
|
||||
const initialState: AskiaState = {
|
||||
status: "idle",
|
||||
error: null,
|
||||
htmlContent: null,
|
||||
};
|
||||
|
||||
const askIaSlice = createSlice({
|
||||
name: "askIa",
|
||||
initialState,
|
||||
reducers: {
|
||||
resetAskIa: (state) => {
|
||||
state.status = "idle";
|
||||
state.error = null;
|
||||
state.htmlContent = null;
|
||||
}
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
/**
|
||||
* Get Sage Builder Dashboard
|
||||
*/
|
||||
builder.addCase(getAskIa.fulfilled, (state, action) => {
|
||||
state.htmlContent = action.payload;
|
||||
state.error = null;
|
||||
state.status = "succeeded";
|
||||
});
|
||||
|
||||
builder.addCase(getAskIa.pending, (state) => {
|
||||
state.status = "loading";
|
||||
state.error = null;
|
||||
});
|
||||
|
||||
builder.addCase(getAskIa.rejected, (state, action) => {
|
||||
state.status = "failed";
|
||||
state.error = action.error.message || "Une erreur est survenue";
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { resetAskIa } = askIaSlice.actions;
|
||||
export default askIaSlice.reducer;
|
||||
28
src/store/features/ask-ia/thunk.ts
Normal file
28
src/store/features/ask-ia/thunk.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
|
||||
/**
|
||||
* Fetch Ask IA HTML
|
||||
*/
|
||||
export const getAskIa = createAsyncThunk(
|
||||
'askIa/getAskIa',
|
||||
async (): Promise<string> => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://api.sage-ai-studio.webexpr.dev/api/embed/ask?mode=light&lang=fr&productSlug=sage100dataven&model=mistral-small',
|
||||
{
|
||||
headers: {
|
||||
'Authorization': 'Bearer sk_live_bef70d4dd6bc671fd815d21104d5dce8'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Erreur de chargement du dashboard');
|
||||
}
|
||||
|
||||
return await response.text();
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
);
|
||||
5
src/store/features/ask-ia/type.ts
Normal file
5
src/store/features/ask-ia/type.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface AskiaState {
|
||||
status: 'idle' | 'loading' | 'succeeded' | 'failed';
|
||||
error: string | null;
|
||||
htmlContent: string | null;
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import userReducer from "./features/user/slice";
|
|||
import entrepriseReducer from "./features/entreprise/slice"
|
||||
import sageBuilderReducer from "./features/sage-builder/slice";
|
||||
import reglementReducer from "./features/reglement/slice";
|
||||
import askIaReducer from "./features/ask-ia/slice";
|
||||
|
||||
import {
|
||||
FLUSH,
|
||||
|
|
@ -48,7 +49,8 @@ const appReducer = combineReducers({
|
|||
user: userReducer,
|
||||
entreprise: entrepriseReducer,
|
||||
sageBuilder: sageBuilderReducer,
|
||||
reglement: reglementReducer
|
||||
reglement: reglementReducer,
|
||||
askIa: askIaReducer,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
|||
Loading…
Reference in a new issue