306 lines
No EOL
9.9 KiB
TypeScript
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; |