feat: Initialize OmniSupport AI project structure
Sets up the basic project structure for OmniSupport AI, including: - Vite build tool configuration. - React and necessary dependencies. - TypeScript configuration. - Basic HTML and root component setup. - Initial type definitions and mock data for core entities like tickets, agents, and queues. - A README with setup instructions. - A .gitignore file for common build artifacts and development files.
This commit is contained in:
312
App.tsx
Normal file
312
App.tsx
Normal file
@@ -0,0 +1,312 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { AgentDashboard } from './components/AgentDashboard';
|
||||
import { ClientPortal } from './components/ClientPortal';
|
||||
import { AuthScreen } from './components/AuthScreen';
|
||||
import { ToastContainer, ToastMessage, ToastType } from './components/Toast';
|
||||
import { INITIAL_KB, INITIAL_QUEUES, INITIAL_SETTINGS, INITIAL_TICKETS, MOCK_AGENTS, MOCK_CLIENT_USERS, MOCK_SURVEYS } from './constants';
|
||||
import { Agent, AppSettings, AppState, ClientUser, KBArticle, Ticket, TicketStatus, SurveyResult, TicketQueue, ChatMessage } from './types';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [state, setState] = useState<AppState>({
|
||||
tickets: INITIAL_TICKETS,
|
||||
articles: INITIAL_KB,
|
||||
agents: MOCK_AGENTS,
|
||||
queues: INITIAL_QUEUES,
|
||||
surveys: MOCK_SURVEYS,
|
||||
clientUsers: MOCK_CLIENT_USERS,
|
||||
settings: INITIAL_SETTINGS,
|
||||
currentUser: null,
|
||||
userRole: 'guest'
|
||||
});
|
||||
|
||||
const [toasts, setToasts] = useState<ToastMessage[]>([]);
|
||||
|
||||
const showToast = (message: string, type: ToastType = 'info') => {
|
||||
const id = Date.now().toString();
|
||||
setToasts(prev => [...prev, { id, message, type }]);
|
||||
};
|
||||
|
||||
const removeToast = (id: string) => {
|
||||
setToasts(prev => prev.filter(t => t.id !== id));
|
||||
};
|
||||
|
||||
// --- Auth Management ---
|
||||
const handleClientLogin = (email: string, pass: string): boolean => {
|
||||
const user = state.clientUsers.find(u => u.email === email && u.password === pass);
|
||||
if (user) {
|
||||
setState(prev => ({ ...prev, currentUser: user, userRole: 'client' }));
|
||||
showToast(`Bentornato, ${user.name}!`, 'success');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleAgentLogin = (email: string, pass: string): boolean => {
|
||||
const agent = state.agents.find(a => a.email === email && a.password === pass);
|
||||
if (agent) {
|
||||
// Set the specific role from the agent object (agent, supervisor, superadmin)
|
||||
setState(prev => ({ ...prev, currentUser: agent, userRole: agent.role }));
|
||||
showToast(`Accesso effettuato come ${agent.role}`, 'success');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleClientRegister = (name: string, email: string, pass: string, company: string) => {
|
||||
const newUser: ClientUser = {
|
||||
id: `u${Date.now()}`,
|
||||
name,
|
||||
email,
|
||||
password: pass,
|
||||
company,
|
||||
status: 'active'
|
||||
};
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
clientUsers: [...prev.clientUsers, newUser],
|
||||
currentUser: newUser,
|
||||
userRole: 'client'
|
||||
}));
|
||||
showToast("Registrazione completata con successo!", 'success');
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
setState(prev => ({ ...prev, currentUser: null, userRole: 'guest' }));
|
||||
showToast("Logout effettuato", 'info');
|
||||
};
|
||||
|
||||
// --- Ticket Management ---
|
||||
const createTicket = (ticketData: Omit<Ticket, 'id' | 'createdAt' | 'messages' | 'status'>) => {
|
||||
const newTicket: Ticket = {
|
||||
...ticketData,
|
||||
id: `T-${1000 + state.tickets.length + 1}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
status: TicketStatus.OPEN,
|
||||
messages: [],
|
||||
attachments: ticketData.attachments || []
|
||||
};
|
||||
setState(prev => ({ ...prev, tickets: [newTicket, ...prev.tickets] }));
|
||||
showToast("Ticket creato correttamente", 'success');
|
||||
};
|
||||
|
||||
const replyToTicket = (ticketId: string, message: string) => {
|
||||
const newMessage: ChatMessage = {
|
||||
id: `m-${Date.now()}`,
|
||||
role: 'user',
|
||||
content: message,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
tickets: prev.tickets.map(t =>
|
||||
t.id === ticketId
|
||||
? { ...t, messages: [...t.messages, newMessage], status: t.status === TicketStatus.RESOLVED ? TicketStatus.OPEN : t.status }
|
||||
: t
|
||||
)
|
||||
}));
|
||||
// Toast handled in component for better UX or here
|
||||
};
|
||||
|
||||
const updateTicketStatus = (id: string, status: TicketStatus) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
tickets: prev.tickets.map(t => t.id === id ? { ...t, status } : t)
|
||||
}));
|
||||
showToast(`Stato ticket aggiornato a ${status}`, 'info');
|
||||
};
|
||||
|
||||
const updateTicketAgent = (id: string, agentId: string) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
tickets: prev.tickets.map(t => t.id === id ? { ...t, assignedAgentId: agentId } : t)
|
||||
}));
|
||||
showToast("Agente assegnato con successo", 'success');
|
||||
};
|
||||
|
||||
// --- KB Management ---
|
||||
const addArticle = (article: KBArticle) => {
|
||||
if (!state.settings.features.kbEnabled) {
|
||||
showToast("La funzionalità Knowledge Base è disabilitata.", 'error');
|
||||
return;
|
||||
}
|
||||
if (state.articles.length >= state.settings.features.maxKbArticles) {
|
||||
showToast(`Limite massimo di articoli (${state.settings.features.maxKbArticles}) raggiunto.`, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for AI Quota if manually triggering AI generation logic
|
||||
if (article.source === 'ai') {
|
||||
if (!state.settings.features.aiKnowledgeAgentEnabled) {
|
||||
showToast("L'Agente AI per la Knowledge Base è disabilitato.", 'error');
|
||||
return;
|
||||
}
|
||||
const aiCount = state.articles.filter(a => a.source === 'ai').length;
|
||||
if (aiCount >= state.settings.features.maxAiGeneratedArticles) {
|
||||
showToast(`Limite creazione articoli AI (${state.settings.features.maxAiGeneratedArticles}) raggiunto.`, 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
articles: [article, ...prev.articles]
|
||||
}));
|
||||
showToast("Articolo aggiunto con successo", 'success');
|
||||
};
|
||||
|
||||
const updateArticle = (updatedArticle: KBArticle) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
articles: prev.articles.map(a => a.id === updatedArticle.id ? updatedArticle : a)
|
||||
}));
|
||||
showToast("Articolo aggiornato", 'success');
|
||||
};
|
||||
|
||||
// --- Survey Management ---
|
||||
const submitSurvey = (surveyData: Omit<SurveyResult, 'id' | 'timestamp'>) => {
|
||||
const newSurvey: SurveyResult = {
|
||||
...surveyData,
|
||||
id: `s${Date.now()}`,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
surveys: [...prev.surveys, newSurvey]
|
||||
}));
|
||||
showToast("Grazie per il tuo feedback!", 'success');
|
||||
};
|
||||
|
||||
// --- Settings Management ---
|
||||
const addAgent = (agent: Agent) => {
|
||||
// Quota Validation
|
||||
if (agent.role === 'supervisor') {
|
||||
const supervisorCount = state.agents.filter(a => a.role === 'supervisor').length;
|
||||
if (supervisorCount >= state.settings.features.maxSupervisors) {
|
||||
showToast(`Limite Supervisor (${state.settings.features.maxSupervisors}) raggiunto.`, 'error');
|
||||
return;
|
||||
}
|
||||
} else if (agent.role === 'agent') {
|
||||
const agentCount = state.agents.filter(a => a.role === 'agent').length;
|
||||
if (agentCount >= state.settings.features.maxAgents) {
|
||||
showToast(`Limite Agenti (${state.settings.features.maxAgents}) raggiunto.`, 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setState(prev => ({ ...prev, agents: [...prev.agents, agent] }));
|
||||
showToast("Nuovo agente aggiunto", 'success');
|
||||
};
|
||||
|
||||
const updateAgent = (agent: Agent) => {
|
||||
setState(prev => ({ ...prev, agents: prev.agents.map(a => a.id === agent.id ? agent : a) }));
|
||||
showToast("Dati agente aggiornati", 'success');
|
||||
};
|
||||
|
||||
const removeAgent = (id: string) => {
|
||||
setState(prev => ({ ...prev, agents: prev.agents.filter(a => a.id !== id) }));
|
||||
showToast("Agente rimosso", 'info');
|
||||
};
|
||||
|
||||
const addClientUser = (user: ClientUser) => {
|
||||
setState(prev => ({ ...prev, clientUsers: [...prev.clientUsers, user] }));
|
||||
showToast("Utente aggiunto", 'success');
|
||||
};
|
||||
|
||||
const updateClientUser = (user: ClientUser) => {
|
||||
setState(prev => ({ ...prev, clientUsers: prev.clientUsers.map(u => u.id === user.id ? user : u) }));
|
||||
showToast("Utente aggiornato", 'success');
|
||||
};
|
||||
|
||||
const removeClientUser = (id: string) => {
|
||||
setState(prev => ({ ...prev, clientUsers: prev.clientUsers.filter(u => u.id !== id) }));
|
||||
showToast("Utente rimosso", 'info');
|
||||
};
|
||||
|
||||
const updateSettings = (newSettings: AppSettings) => {
|
||||
setState(prev => ({ ...prev, settings: newSettings }));
|
||||
// Toast triggered in component
|
||||
};
|
||||
|
||||
// --- Queue Management ---
|
||||
const addQueue = (queue: TicketQueue) => {
|
||||
setState(prev => ({ ...prev, queues: [...prev.queues, queue] }));
|
||||
showToast("Coda creata", 'success');
|
||||
};
|
||||
|
||||
const removeQueue = (id: string) => {
|
||||
setState(prev => ({ ...prev, queues: prev.queues.filter(q => q.id !== id) }));
|
||||
showToast("Coda rimossa", 'info');
|
||||
};
|
||||
|
||||
|
||||
// Render Logic
|
||||
if (!state.currentUser) {
|
||||
return (
|
||||
<>
|
||||
<AuthScreen
|
||||
settings={state.settings}
|
||||
onClientLogin={handleClientLogin}
|
||||
onAgentLogin={handleAgentLogin}
|
||||
onClientRegister={handleClientRegister}
|
||||
/>
|
||||
<ToastContainer toasts={toasts} removeToast={removeToast} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Filter Tickets for Agent: Only show tickets from assigned queues
|
||||
const isAgentOrSupervisor = state.userRole === 'agent' || state.userRole === 'supervisor';
|
||||
const agentTickets = isAgentOrSupervisor
|
||||
? state.tickets.filter(t => (state.currentUser as Agent).queues.includes(t.queue))
|
||||
: state.tickets; // Superadmin sees all
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 text-gray-900 font-sans" style={{ '--brand-color': state.settings.branding.primaryColor } as React.CSSProperties}>
|
||||
|
||||
{state.userRole === 'client' ? (
|
||||
<ClientPortal
|
||||
currentUser={state.currentUser as ClientUser}
|
||||
articles={state.settings.features.kbEnabled ? state.articles : []}
|
||||
tickets={state.tickets.filter(t => t.customerName === state.currentUser?.name)}
|
||||
queues={state.queues}
|
||||
onCreateTicket={createTicket}
|
||||
onReplyTicket={replyToTicket}
|
||||
onSubmitSurvey={submitSurvey}
|
||||
onLogout={handleLogout}
|
||||
showToast={showToast}
|
||||
/>
|
||||
) : (
|
||||
<AgentDashboard
|
||||
currentUser={state.currentUser as Agent}
|
||||
tickets={agentTickets}
|
||||
articles={state.settings.features.kbEnabled ? state.articles : []}
|
||||
agents={state.agents}
|
||||
queues={state.queues}
|
||||
surveys={state.surveys}
|
||||
clientUsers={state.clientUsers}
|
||||
settings={state.settings}
|
||||
updateTicketStatus={updateTicketStatus}
|
||||
updateTicketAgent={updateTicketAgent}
|
||||
addArticle={addArticle}
|
||||
updateArticle={updateArticle}
|
||||
addAgent={addAgent}
|
||||
updateAgent={updateAgent}
|
||||
removeAgent={removeAgent}
|
||||
addClientUser={addClientUser}
|
||||
updateClientUser={updateClientUser}
|
||||
removeClientUser={removeClientUser}
|
||||
updateSettings={updateSettings}
|
||||
addQueue={addQueue}
|
||||
removeQueue={removeQueue}
|
||||
onLogout={handleLogout}
|
||||
showToast={showToast}
|
||||
/>
|
||||
)}
|
||||
<ToastContainer toasts={toasts} removeToast={removeToast} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user