Vendaweb-portal/components/checkout/AddressStep.tsx

418 lines
16 KiB
TypeScript
Raw Normal View History

2026-01-08 12:09:16 +00:00
import React, { useState, useEffect } from "react";
import { CustomerAddress } from "../../src/services/customer.service";
import {
MapPin,
Plus,
Pencil,
Trash2,
ArrowLeft,
ArrowRight,
CheckCircle,
} from "lucide-react";
import AddressSelectionModal from "./AddressSelectionModal";
import AddressFormModal from "./AddressFormModal";
import ConfirmDialog from "../ConfirmDialog";
interface AddressStepProps {
addressForm: {
zipCode: string;
address: string;
number: string;
city: string;
state: string;
complement: string;
referencePoint: string;
note: string;
};
addressErrors: Record<string, string>;
selectedAddress: CustomerAddress | null;
customerId: number | null;
onFormChange: (field: string, value: string) => void;
onShowAddressModal: () => void;
onRemoveAddress: () => void;
onSelectAddress: (address: CustomerAddress) => void;
onPrevious: () => void;
onNext: () => void;
}
const AddressStep: React.FC<AddressStepProps> = ({
addressForm,
addressErrors,
selectedAddress,
customerId,
onFormChange,
onShowAddressModal,
onRemoveAddress,
onSelectAddress,
onPrevious,
onNext,
}) => {
const [showSelectionModal, setShowSelectionModal] = useState(false);
const [showFormModal, setShowFormModal] = useState(false);
const [showRemoveDialog, setShowRemoveDialog] = useState(false);
const [editingAddress, setEditingAddress] = useState<CustomerAddress | null>(
null
);
const formatAddress = (address: CustomerAddress) => {
const parts = [];
if (address.address) parts.push(address.address);
if (address.number) parts.push(address.number);
if (address.complement) parts.push(address.complement);
return parts.join(", ");
};
const handleSelectFromList = () => {
if (!customerId) {
console.warn(
"AddressStep: customerId não fornecido. Não é possível abrir o modal de seleção de endereços."
);
return;
}
console.log(
"AddressStep: Abrindo modal de seleção de endereços para customerId:",
customerId
);
setShowSelectionModal(true);
};
const handleCreateNew = () => {
setEditingAddress(null);
setShowFormModal(true);
};
const handleEdit = () => {
if (selectedAddress) {
setEditingAddress(selectedAddress);
setShowFormModal(true);
}
};
const handleAddressSelected = (address: CustomerAddress) => {
onSelectAddress(address);
setShowSelectionModal(false);
};
const handleAddressSaved = (address: CustomerAddress) => {
onSelectAddress(address);
setShowFormModal(false);
setEditingAddress(null);
};
return (
<>
<div className="animate-fade-in">
<div className="flex justify-between items-center mb-6">
<h3 className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-400">
Endereço de entrega
</h3>
<div className="flex gap-2">
<button
onClick={handleSelectFromList}
className="flex items-center gap-2 bg-slate-100 px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest text-[#002147] hover:bg-slate-200 transition-colors"
>
<MapPin className="w-4 h-4" />
Selecionar
</button>
<button
onClick={handleCreateNew}
className="flex items-center gap-2 bg-slate-100 px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest text-[#002147] hover:bg-slate-200 transition-colors"
>
<Plus className="w-4 h-4" />
Novo
</button>
{selectedAddress && (
<>
<button
onClick={handleEdit}
className="flex items-center gap-2 bg-[#002147] text-white px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest hover:bg-[#003366] transition-colors"
>
<Pencil className="w-4 h-4" />
Editar
</button>
<button
onClick={() => setShowRemoveDialog(true)}
className="flex items-center gap-2 bg-red-100 text-red-600 px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest hover:bg-red-200 transition-colors"
>
<Trash2 className="w-4 h-4" />
Remover
</button>
</>
)}
</div>
</div>
{selectedAddress ? (
/* Endereço Selecionado */
<div className="bg-gradient-to-br from-orange-50 to-orange-100/50 rounded-2xl p-6 border-2 border-orange-200 mb-6">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2 mb-3">
<CheckCircle className="w-5 h-5 text-green-500" />
<h4 className="font-black text-slate-800 text-lg">
{selectedAddress.addressType || "Endereço"}
</h4>
{selectedAddress.isPrimary && (
<span className="px-2 py-0.5 bg-green-100 text-green-700 rounded-full text-[10px] font-black uppercase">
Principal
</span>
)}
</div>
<p className="text-sm font-bold text-slate-700 mb-2">
{formatAddress(selectedAddress)}
</p>
<p className="text-xs text-slate-600 mb-1">
{selectedAddress.neighborhood &&
`${selectedAddress.neighborhood} - `}
{selectedAddress.city && `${selectedAddress.city}`}
{selectedAddress.state && `/${selectedAddress.state}`}
</p>
{selectedAddress.zipCode && (
<p className="text-xs text-slate-600">
CEP: {selectedAddress.zipCode}
</p>
)}
{selectedAddress.referencePoint && (
<p className="text-xs text-slate-500 mt-2">
<strong>Referência:</strong>{" "}
{selectedAddress.referencePoint}
</p>
)}
</div>
</div>
</div>
) : (
/* Sem Endereço Selecionado */
<div className="bg-slate-50 rounded-2xl p-8 border-2 border-dashed border-slate-300 mb-6 text-center">
<div className="w-20 h-20 bg-slate-200 rounded-full flex items-center justify-center mx-auto mb-4">
<MapPin className="w-10 h-10 text-slate-400" />
</div>
<p className="text-lg font-bold text-slate-600 mb-2">
Nenhum endereço selecionado
</p>
<p className="text-sm text-slate-500 mb-6">
Selecione um endereço existente ou cadastre um novo endereço de
entrega
</p>
<div className="flex gap-3 justify-center">
<button
onClick={handleSelectFromList}
className="flex items-center gap-2 bg-[#002147] text-white px-6 py-3 rounded-xl font-black uppercase text-xs tracking-widest hover:bg-[#001a36] transition-all"
>
<MapPin className="w-4 h-4" />
Selecionar Endereço
</button>
<button
onClick={handleCreateNew}
className="flex items-center gap-2 bg-white border-2 border-[#002147] text-[#002147] px-6 py-3 rounded-xl font-black uppercase text-xs tracking-widest hover:bg-[#002147] hover:text-white transition-all"
>
<Plus className="w-4 h-4" />
Cadastrar Novo
</button>
</div>
</div>
)}
{/* Formulário de Endereço (apenas quando há endereço selecionado) */}
{selectedAddress && (
<form className="space-y-4 mt-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
CEP
</label>
<input
type="text"
value={addressForm.zipCode}
onChange={(e) => onFormChange("zipCode", e.target.value)}
className={`w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border ${
addressErrors.zipCode
? "border-red-500"
: "border-slate-200"
} focus:outline-none focus:ring-2 focus:ring-orange-500/20`}
/>
{addressErrors.zipCode && (
<p className="text-red-500 text-xs mt-1">
{addressErrors.zipCode}
</p>
)}
</div>
<div className="md:col-span-2">
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
endereço
</label>
<input
type="text"
value={addressForm.address}
onChange={(e) => onFormChange("address", e.target.value)}
className={`w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border ${
addressErrors.address
? "border-red-500"
: "border-slate-200"
} focus:outline-none focus:ring-2 focus:ring-orange-500/20`}
/>
{addressErrors.address && (
<p className="text-red-500 text-xs mt-1">
{addressErrors.address}
</p>
)}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
número
</label>
<input
type="text"
value={addressForm.number}
onChange={(e) => onFormChange("number", e.target.value)}
className={`w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border ${
addressErrors.number ? "border-red-500" : "border-slate-200"
} focus:outline-none focus:ring-2 focus:ring-orange-500/20`}
/>
{addressErrors.number && (
<p className="text-red-500 text-xs mt-1">
{addressErrors.number}
</p>
)}
</div>
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
cidade
</label>
<input
type="text"
value={addressForm.city}
onChange={(e) => onFormChange("city", e.target.value)}
className={`w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border ${
addressErrors.city ? "border-red-500" : "border-slate-200"
} focus:outline-none focus:ring-2 focus:ring-orange-500/20`}
/>
{addressErrors.city && (
<p className="text-red-500 text-xs mt-1">
{addressErrors.city}
</p>
)}
</div>
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
estado
</label>
<input
type="text"
value={addressForm.state}
onChange={(e) => onFormChange("state", e.target.value)}
className={`w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border ${
addressErrors.state ? "border-red-500" : "border-slate-200"
} focus:outline-none focus:ring-2 focus:ring-orange-500/20`}
/>
{addressErrors.state && (
<p className="text-red-500 text-xs mt-1">
{addressErrors.state}
</p>
)}
</div>
</div>
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
complemento
</label>
<input
type="text"
value={addressForm.complement}
onChange={(e) => onFormChange("complement", e.target.value)}
className="w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border border-slate-200 focus:outline-none focus:ring-2 focus:ring-orange-500/20"
/>
</div>
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
Ponto de referência
</label>
<input
type="text"
value={addressForm.referencePoint}
onChange={(e) => onFormChange("referencePoint", e.target.value)}
className="w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border border-slate-200 focus:outline-none focus:ring-2 focus:ring-orange-500/20"
/>
</div>
<div>
<label className="block text-[10px] font-black uppercase text-slate-400 mb-2">
observações
</label>
<input
type="text"
value={addressForm.note}
onChange={(e) => onFormChange("note", e.target.value)}
className="w-full px-4 py-3 bg-slate-50 rounded-xl font-bold text-[#002147] border border-slate-200 focus:outline-none focus:ring-2 focus:ring-orange-500/20"
/>
</div>
</form>
)}
<div className="flex justify-between gap-2 mt-6">
<button
type="button"
onClick={onPrevious}
className="flex items-center gap-2 bg-slate-100 px-6 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest text-[#002147] hover:bg-slate-200 transition-colors"
>
<ArrowLeft className="w-4 h-4" />
Retornar
</button>
<button
type="button"
onClick={onNext}
className="flex items-center gap-2 bg-[#002147] text-white px-6 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest hover:bg-[#003366] transition-colors"
>
Avançar
<ArrowRight className="w-4 h-4" />
</button>
</div>
</div>
{/* Modais */}
<AddressSelectionModal
isOpen={showSelectionModal}
customerId={customerId}
onClose={() => setShowSelectionModal(false)}
onSelect={handleAddressSelected}
onCreateNew={() => {
setShowSelectionModal(false);
handleCreateNew();
}}
/>
<AddressFormModal
isOpen={showFormModal}
customerId={customerId}
address={editingAddress}
onClose={() => {
setShowFormModal(false);
setEditingAddress(null);
}}
onSave={handleAddressSaved}
/>
<ConfirmDialog
isOpen={showRemoveDialog}
onClose={() => setShowRemoveDialog(false)}
onConfirm={() => {
onRemoveAddress();
setShowRemoveDialog(false);
}}
type="delete"
title="Remover Endereço"
message="Tem certeza que deseja remover este endereço de entrega? Esta ação não pode ser desfeita."
confirmText="Remover"
cancelText="Cancelar"
/>
</>
);
};
export default AddressStep;