Update ClientPortal.tsx

This commit is contained in:
fcarraUniSa
2026-02-18 10:16:23 +01:00
committed by GitHub
parent 7ce1e0e985
commit 6f79ec3f60

View File

@@ -1,4 +1,5 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
import { Ticket, KBArticle, ChatMessage, TicketPriority, TicketStatus, SurveyResult, Attachment, ClientUser, TicketQueue, AppSettings } from '../types'; import { Ticket, KBArticle, ChatMessage, TicketPriority, TicketStatus, SurveyResult, Attachment, ClientUser, TicketQueue, AppSettings } from '../types';
import { getSupportResponse } from '../services/geminiService'; import { getSupportResponse } from '../services/geminiService';
import { ToastType } from './Toast'; import { ToastType } from './Toast';
@@ -39,6 +40,23 @@ interface ClientPortalProps {
showToast: (message: string, type: ToastType) => void; showToast: (message: string, type: ToastType) => void;
} }
// Configurazione stili per ReactMarkdown
const MarkdownComponents = {
h1: (props: any) => <h1 className="text-2xl font-bold text-gray-900 mb-4 mt-6 border-b border-gray-100 pb-2" {...props} />,
h2: (props: any) => <h2 className="text-xl font-bold text-gray-800 mb-3 mt-5" {...props} />,
h3: (props: any) => <h3 className="text-lg font-semibold text-gray-800 mb-2 mt-4" {...props} />,
p: (props: any) => <p className="text-gray-600 mb-4 leading-relaxed" {...props} />,
ul: (props: any) => <ul className="list-disc pl-5 mb-4 space-y-2 text-gray-600" {...props} />,
ol: (props: any) => <ol className="list-decimal pl-5 mb-4 space-y-2 text-gray-600" {...props} />,
li: (props: any) => <li className="pl-1" {...props} />,
strong: (props: any) => <strong className="font-bold text-gray-900" {...props} />,
blockquote: (props: any) => <blockquote className="border-l-4 border-brand-200 pl-4 italic text-gray-500 my-4 bg-gray-50 py-2 pr-2 rounded-r" {...props} />,
a: (props: any) => <a className="text-brand-600 underline hover:text-brand-800 transition-colors" target="_blank" rel="noopener noreferrer" {...props} />,
code: (props: any) => <code className="bg-gray-100 text-pink-600 px-1.5 py-0.5 rounded text-sm font-mono border border-gray-200" {...props} />,
pre: (props: any) => <pre className="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto mb-4 text-sm" {...props} />,
hr: (props: any) => <hr className="my-6 border-gray-200" {...props} />,
};
export const ClientPortal: React.FC<ClientPortalProps> = ({ export const ClientPortal: React.FC<ClientPortalProps> = ({
currentUser, currentUser,
articles, articles,
@@ -524,7 +542,16 @@ export const ClientPortal: React.FC<ClientPortalProps> = ({
> >
<span className="text-xs font-bold text-brand-600 bg-brand-50 px-2 py-1 rounded-md uppercase tracking-wide">{article.category}</span> <span className="text-xs font-bold text-brand-600 bg-brand-50 px-2 py-1 rounded-md uppercase tracking-wide">{article.category}</span>
<h3 className="text-lg font-bold text-gray-800 my-2">{article.title}</h3> <h3 className="text-lg font-bold text-gray-800 my-2">{article.title}</h3>
<p className="text-gray-600 text-sm line-clamp-3">{article.content}</p> <div className="text-gray-600 text-sm line-clamp-3">
<ReactMarkdown components={{
h1: ({node, ...props}) => <span className="font-bold" {...props} />,
h2: ({node, ...props}) => <span className="font-bold" {...props} />,
p: ({node, ...props}) => <span {...props} />,
li: ({node, ...props}) => <span className="mr-1" {...props} />,
}}>
{article.content}
</ReactMarkdown>
</div>
</div> </div>
))} ))}
</div> </div>
@@ -534,11 +561,28 @@ export const ClientPortal: React.FC<ClientPortalProps> = ({
{/* KB Modal */} {/* KB Modal */}
{viewingArticle && ( {viewingArticle && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50"> <div className="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
<div className="bg-white p-8 rounded-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto relative"> <div className="bg-white p-8 rounded-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto relative animate-fade-in-up">
<button onClick={() => setViewingArticle(null)} className="absolute top-4 right-4"><X/></button> <button onClick={() => setViewingArticle(null)} className="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition"><X/></button>
<h2 className="text-2xl font-bold mb-4">{viewingArticle.title}</h2> <h2 className="text-3xl font-extrabold mb-2 text-gray-900">{viewingArticle.title}</h2>
<div className="prose">{viewingArticle.content}</div> <div className="flex items-center gap-2 mb-6 text-sm text-gray-500">
{viewingArticle.type === 'url' && <a href={viewingArticle.url} target="_blank" className="text-blue-600 underline mt-4 block">Vai alla risorsa esterna</a>} <span className="bg-brand-50 text-brand-700 px-2 py-0.5 rounded font-semibold">{viewingArticle.category}</span>
<span></span>
<span>Aggiornato il {viewingArticle.lastUpdated}</span>
</div>
<div className="prose prose-blue max-w-none text-gray-700">
<ReactMarkdown components={MarkdownComponents}>
{viewingArticle.content}
</ReactMarkdown>
</div>
{viewingArticle.type === 'url' && (
<div className="mt-8 pt-4 border-t border-gray-100">
<a href={viewingArticle.url} target="_blank" className="inline-flex items-center text-white bg-brand-600 px-4 py-2 rounded-lg hover:bg-brand-700 transition font-medium">
<ExternalLink className="w-4 h-4 mr-2"/> Vai alla risorsa originale
</a>
</div>
)}
</div> </div>
</div> </div>
)} )}
@@ -579,7 +623,18 @@ export const ClientPortal: React.FC<ClientPortalProps> = ({
? 'bg-brand-600 text-white rounded-tr-none' ? 'bg-brand-600 text-white rounded-tr-none'
: 'bg-white text-gray-800 rounded-tl-none border border-gray-100' : 'bg-white text-gray-800 rounded-tl-none border border-gray-100'
}`}> }`}>
{msg.content} {/* Render Markdown in chat messages too for consistency if assistant */}
{msg.role === 'assistant' ? (
<ReactMarkdown components={{
p: ({node, ...props}) => <p className="mb-1 last:mb-0" {...props} />,
ul: ({node, ...props}) => <ul className="list-disc pl-4 mb-2" {...props} />,
li: ({node, ...props}) => <li className="mb-0.5" {...props} />,
a: ({node, ...props}) => <a className="underline font-semibold" target="_blank" {...props} />,
strong: ({node, ...props}) => <strong className="font-bold" {...props} />
}}>{msg.content}</ReactMarkdown>
) : (
msg.content
)}
</div> </div>
</div> </div>
))} ))}