saveinmed/frontend/src/components/PedidoForm.tsx

306 lines
No EOL
9.9 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Models } from '@/lib/appwrite';
import { EmpresaData, empresaService } from '@/services/empresaService';
import { PedidoData } from '@/services/pedidoService';
import { usuarioService } from '@/services/usuarioService';
interface PedidoFormProps {
onSubmit: (data: PedidoData) => Promise<boolean>;
onCancel?: () => void;
initialData?: Models.Document | null;
loading?: boolean;
statusOptions: string[];
}
type RelationRef = { $id?: string };
type PedidoDocument = Models.Document &
Partial<PedidoData> & {
comprador?: string | RelationRef;
vendedor?: string[] | RelationRef | string;
};
type UsuarioOption = Models.Document & {
'nome-civil'?: string;
};
type EmpresaOption = EmpresaData & {
$id?: string;
};
const buildEmptyPedido = (): PedidoData => ({
$id: '',
$createdAt: '',
$updatedAt: '',
status: '',
'valor-total': 0,
itens: [],
qtdade: [],
comprador: '',
vendedor: [],
});
const getRelationId = (value: string | RelationRef | undefined): string => {
if (typeof value === 'string') {
return value;
}
if (value && typeof value === 'object') {
return value.$id || '';
}
return '';
};
const PedidoForm: React.FC<PedidoFormProps> = ({
onSubmit,
onCancel,
initialData,
loading = false,
statusOptions,
}) => {
const [formData, setFormData] = useState<PedidoData>(buildEmptyPedido);
const [itensText, setItensText] = useState('');
const [usuarios, setUsuarios] = useState<UsuarioOption[]>([]);
const [empresas, setEmpresas] = useState<EmpresaOption[]>([]);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
useEffect(() => {
if (!initialData) {
setFormData(buildEmptyPedido());
setItensText('');
return;
}
const document = initialData as PedidoDocument;
const vendedor = Array.isArray(document.vendedor)
? document.vendedor
: [getRelationId(document.vendedor)].filter(Boolean);
setFormData({
$id: document.$id,
$createdAt: document.$createdAt,
$updatedAt: document.$updatedAt,
status: document.status || '',
'valor-total': document['valor-total'] || 0,
itens: document.itens || [],
qtdade: document.qtdade || [],
comprador: getRelationId(document.comprador),
vendedor,
});
setItensText((document.itens || []).join('\n'));
}, [initialData]);
useEffect(() => {
const load = async () => {
const uRes = await usuarioService.listar(1, 50);
if (uRes.success) {
setUsuarios((uRes.documents || []) as UsuarioOption[]);
}
const eRes = await empresaService.listar(1, 50);
if (eRes.success) {
setEmpresas((eRes.documents || []) as EmpresaOption[]);
}
};
void load();
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const itens = itensText
.split('\n')
.map((item) => item.trim())
.filter(Boolean);
const success = await onSubmit({ ...formData, itens });
if (success) {
setMessage({
type: 'success',
text: initialData ? 'Pedido atualizado com sucesso!' : 'Pedido cadastrado com sucesso!',
});
if (!initialData) {
setFormData(buildEmptyPedido());
setItensText('');
}
setTimeout(() => setMessage(null), 3000);
return;
}
setMessage({ type: 'error', text: initialData ? 'Erro ao atualizar pedido' : 'Erro ao cadastrar pedido' });
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
if (name === 'itens') {
setItensText(value);
} else if (name === 'valor-total') {
setFormData((prev) => ({ ...prev, [name]: Number.parseFloat(value) || 0 }));
} else if (name === 'vendedor') {
setFormData((prev) => ({ ...prev, vendedor: value ? [value] : [] }));
} else {
setFormData((prev) => ({ ...prev, [name]: value }));
}
if (message) {
setMessage(null);
}
};
return (
<div className="bg-white rounded-lg shadow-md p-6">
<div className="mb-6">
<h2 className="text-2xl font-bold text-gray-900 mb-2">
{initialData ? 'Editar Pedido' : 'Novo Pedido'}
</h2>
<p className="text-gray-600">
{initialData ? 'Atualize os dados do pedido.' : 'Cadastre um novo pedido na plataforma SaveInMed.'}
</p>
</div>
{message && (
<div
className={`mb-4 p-4 rounded-md ${
message.type === 'success'
? 'bg-green-50 text-green-700 border border-green-200'
: 'bg-red-50 text-red-700 border border-red-200'
}`}
>
{message.text}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label htmlFor="comprador" className="block text-sm font-medium text-gray-700 mb-2">
Comprador *
</label>
<select
id="comprador"
name="comprador"
value={formData.comprador}
onChange={handleChange}
required
disabled={loading}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
>
<option value="">Selecione...</option>
{usuarios.map((u) => (
<option key={u.$id} value={u.$id}>
{u['nome-civil'] || u.$id}
</option>
))}
</select>
</div>
<div>
<label htmlFor="vendedor" className="block text-sm font-medium text-gray-700 mb-2">
Vendedor *
</label>
<select
id="vendedor"
name="vendedor"
value={formData.vendedor[0] || ''}
onChange={handleChange}
required
disabled={loading}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
>
<option value="">Selecione...</option>
{usuarios.map((u) => (
<option key={`u-${u.$id}`} value={u.$id}>
{u['nome-civil'] || u.$id}
</option>
))}
{empresas.map((empresa) => (
<option key={`e-${empresa.$id || empresa.id}`} value={empresa.$id || empresa.id}>
{empresa['nome-fantasia'] || empresa.$id || empresa.id}
</option>
))}
</select>
</div>
</div>
<div>
<label htmlFor="itens" className="block text-sm font-medium text-gray-700 mb-2">
Itens (um por linha)
</label>
<textarea
id="itens"
name="itens"
value={itensText}
onChange={handleChange}
disabled={loading}
rows={4}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
placeholder="IDs dos itens"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label htmlFor="valor-total" className="block text-sm font-medium text-gray-700 mb-2">
Valor Total *
</label>
<input
type="number"
step="0.01"
id="valor-total"
name="valor-total"
value={formData['valor-total']}
onChange={handleChange}
required
disabled={loading}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
placeholder="0,00"
/>
</div>
<div>
<label htmlFor="status" className="block text-sm font-medium text-gray-700 mb-2">
Status *
</label>
<select
id="status"
name="status"
value={formData.status}
onChange={handleChange}
required
disabled={loading}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed"
>
<option value="">Selecione...</option>
{statusOptions.map((status) => (
<option key={status} value={status}>
{status}
</option>
))}
</select>
</div>
</div>
<div className="flex gap-4">
<button
type="submit"
disabled={loading || !formData.comprador || !formData.vendedor.length}
className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
>
{loading ? 'Processando...' : initialData ? 'Atualizar' : 'Cadastrar'}
</button>
{onCancel && (
<button
type="button"
onClick={onCancel}
disabled={loading}
className="flex-1 bg-gray-600 text-white py-2 px-4 rounded-md hover:bg-gray-700 focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors cursor-pointer"
>
Cancelar
</button>
)}
</div>
</form>
</div>
);
};
export default PedidoForm;