113 lines
4.6 KiB
TypeScript
113 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import Link from "next/link";
|
|
import { deleteOS } from "@/app/actions";
|
|
|
|
interface OS {
|
|
id: number;
|
|
numero_os: string;
|
|
data_abertura: string;
|
|
fechada: boolean;
|
|
status: string;
|
|
totalproduto: string;
|
|
totalservico: string;
|
|
}
|
|
|
|
export default function OSList({ initialOrders }: { initialOrders: OS[] }) {
|
|
const [orders, setOrders] = useState(initialOrders);
|
|
const [isDeleting, setIsDeleting] = useState<number | null>(null);
|
|
|
|
const handleDelete = async (id: number) => {
|
|
const res = await deleteOS(id);
|
|
if (res.success) {
|
|
setOrders(orders.filter(o => o.id !== id));
|
|
setIsDeleting(null);
|
|
} else {
|
|
alert(res.message);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="os-grid">
|
|
{orders.map((order, i) => (
|
|
<div key={order.id} className="glass-panel os-card animate-slide-up" style={{ animationDelay: `${i * 0.05}s` }}>
|
|
<div className="os-card-header">
|
|
<span className="os-number">Ordem de serviço {order.numero_os}</span>
|
|
<span className="status-badge status-open">
|
|
{order.status || 'Aberta'}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="os-card-body">
|
|
<div className="os-info-item">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
|
|
<line x1="16" y1="2" x2="16" y2="6"></line>
|
|
<line x1="8" y1="2" x2="8" y2="6"></line>
|
|
<line x1="3" y1="10" x2="21" y2="10"></line>
|
|
</svg>
|
|
{order.data_abertura ? new Date(order.data_abertura).toLocaleDateString('pt-BR') : '-'}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="os-card-footer">
|
|
<div className="os-price">
|
|
{((parseFloat(order.totalproduto || '0') + parseFloat(order.totalservico || '0'))).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })}
|
|
</div>
|
|
<div className="os-actions">
|
|
<Link href={`/dashboard/editar-os/${order.id}`} className="action-btn btn-edit" title="Editar">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
|
</svg>
|
|
</Link>
|
|
<button
|
|
onClick={() => setIsDeleting(order.id)}
|
|
className="action-btn btn-delete"
|
|
title="Excluir"
|
|
>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<polyline points="3 6 5 6 21 6"></polyline>
|
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
<line x1="10" y1="11" x2="10" y2="17"></line>
|
|
<line x1="14" y1="11" x2="14" y2="17"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{isDeleting && (
|
|
<div className="modal-overlay">
|
|
<div className="glass-panel modal-content">
|
|
<h3 style={{ marginBottom: '1rem', color: 'var(--text-primary)' }}>Confirmar Exclusão</h3>
|
|
<p style={{ color: 'var(--text-secondary)', marginBottom: '2rem' }}>
|
|
Tem certeza que deseja excluir a Ordem de Serviço <strong>#{orders.find(o => o.id === isDeleting)?.numero_os}</strong>? Esta ação não pode ser desfeita.
|
|
</p>
|
|
<div style={{ display: 'flex', gap: '1rem', justifyContent: 'flex-end' }}>
|
|
<button
|
|
onClick={() => setIsDeleting(null)}
|
|
className="btn-primary"
|
|
style={{ backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-primary)' }}
|
|
>
|
|
Cancelar
|
|
</button>
|
|
<button
|
|
onClick={() => handleDelete(isDeleting)}
|
|
className="btn-primary"
|
|
style={{ backgroundColor: 'var(--accent-danger)' }}
|
|
>
|
|
Excluir
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|