import React, { useEffect, useState } from 'react'; import { CondoService } from '../services/mockDb'; import { Ticket, TicketStatus, TicketPriority, TicketCategory, TicketAttachment } from '../types'; import { MessageSquareWarning, Plus, Search, Filter, Paperclip, X, CheckCircle2, Clock, XCircle, FileIcon, Image as ImageIcon, Film } from 'lucide-react'; export const TicketsPage: React.FC = () => { const user = CondoService.getCurrentUser(); const isAdmin = user?.role === 'admin' || user?.role === 'poweruser'; const [tickets, setTickets] = useState([]); const [loading, setLoading] = useState(true); const [filterStatus, setFilterStatus] = useState('ALL'); const [showModal, setShowModal] = useState(false); const [viewTicket, setViewTicket] = useState(null); const [submitting, setSubmitting] = useState(false); // Form State const [formTitle, setFormTitle] = useState(''); const [formDesc, setFormDesc] = useState(''); const [formCategory, setFormCategory] = useState(TicketCategory.OTHER); const [formPriority, setFormPriority] = useState(TicketPriority.MEDIUM); const [attachments, setAttachments] = useState<{fileName: string, fileType: string, data: string}[]>([]); // File Reading helper const handleFileChange = async (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { const newAttachments = []; for (let i = 0; i < e.target.files.length; i++) { const file = e.target.files[i]; // Check size (e.g. 5MB limit per file for demo safety) if (file.size > 5 * 1024 * 1024) { alert(`Il file ${file.name} è troppo grande (max 5MB)`); continue; } const base64 = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = error => reject(error); }); newAttachments.push({ fileName: file.name, fileType: file.type, data: base64 }); } setAttachments([...attachments, ...newAttachments]); } }; const removeAttachment = (index: number) => { const newAtt = [...attachments]; newAtt.splice(index, 1); setAttachments(newAtt); }; const loadTickets = async () => { setLoading(true); try { const list = await CondoService.getTickets(); setTickets(list); } catch (e) { console.error(e); } finally { setLoading(false); } }; useEffect(() => { loadTickets(); }, []); const handleCreateSubmit = async (e: React.FormEvent) => { e.preventDefault(); setSubmitting(true); try { await CondoService.createTicket({ title: formTitle, description: formDesc, category: formCategory, priority: formPriority, attachments: attachments }); setShowModal(false); // Reset form setFormTitle(''); setFormDesc(''); setAttachments([]); loadTickets(); } catch (e) { alert("Errore creazione ticket"); console.error(e); } finally { setSubmitting(false); } }; const handleStatusUpdate = async (status: TicketStatus) => { if (!viewTicket) return; try { await CondoService.updateTicket(viewTicket.id, { status: status, priority: viewTicket.priority }); // Update local state const updated = { ...viewTicket, status }; setViewTicket(updated); setTickets(tickets.map(t => t.id === updated.id ? updated : t)); } catch (e) { console.error(e); } }; const handleDeleteTicket = async (id: string) => { if (!confirm("Eliminare definitivamente questa segnalazione?")) return; try { await CondoService.deleteTicket(id); setTickets(tickets.filter(t => t.id !== id)); setViewTicket(null); } catch (e) { alert("Impossibile eliminare."); } }; const filteredTickets = tickets.filter(t => filterStatus === 'ALL' || t.status === filterStatus); // Helpers for Badge Colors const getStatusColor = (s: TicketStatus) => { switch(s) { case TicketStatus.OPEN: return 'bg-blue-100 text-blue-700'; case TicketStatus.IN_PROGRESS: return 'bg-yellow-100 text-yellow-700'; case TicketStatus.RESOLVED: return 'bg-green-100 text-green-700'; case TicketStatus.CLOSED: return 'bg-slate-200 text-slate-600'; } }; const getPriorityColor = (p: TicketPriority) => { switch(p) { case TicketPriority.LOW: return 'bg-slate-100 text-slate-600'; case TicketPriority.MEDIUM: return 'bg-blue-50 text-blue-600'; case TicketPriority.HIGH: return 'bg-orange-100 text-orange-600'; case TicketPriority.URGENT: return 'bg-red-100 text-red-600'; } }; const getCategoryLabel = (c: TicketCategory) => { switch(c) { case TicketCategory.MAINTENANCE: return 'Manutenzione'; case TicketCategory.ADMINISTRATIVE: return 'Amministrazione'; case TicketCategory.CLEANING: return 'Pulizie'; case TicketCategory.NOISE: return 'Disturbo'; default: return 'Altro'; } }; const openAttachment = async (ticketId: string, attId: string) => { try { const file = await CondoService.getTicketAttachment(ticketId, attId); // Open base64 in new tab const win = window.open(); if (win) { // Determine display method if (file.fileType.startsWith('image/')) { win.document.write(``); } else if (file.fileType === 'application/pdf') { win.document.write(``); } else { // Download link fallback win.document.write(`Clicca qui per scaricare ${file.fileName}`); } } } catch (e) { alert("Errore apertura file"); } }; return (

Segnalazioni

Gestisci guasti e richieste

{/* Filters */}
{['ALL', 'OPEN', 'IN_PROGRESS', 'RESOLVED', 'CLOSED'].map(status => ( ))}
{/* List */} {loading ? (
Caricamento...
) : filteredTickets.length === 0 ? (

Nessuna segnalazione trovata.

) : (
{filteredTickets.map(ticket => (
setViewTicket(ticket)} className="bg-white p-5 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow cursor-pointer relative group">
{ticket.status.replace('_', ' ')} {ticket.priority}

{ticket.title}

{new Date(ticket.createdAt).toLocaleDateString()} • {getCategoryLabel(ticket.category)}

{ticket.description}

{ticket.userName ? ticket.userName.charAt(0) : '?'}
{ticket.userName || 'Utente'}
{ticket.attachments && ticket.attachments.length > 0 && (
{ticket.attachments.length}
)}
))}
)} {/* CREATE MODAL */} {showModal && (

Nuova Segnalazione

setFormTitle(e.target.value)} required placeholder="Es. Luce scale rotta" />