photum/frontend/components/InstitutionForm.tsx
João Vitor ac274e5c91 feat: melhorias no formulário de eventos e mapa interativo
- Adiciona campo de número de pessoas com validação numérica
- Atualiza tipos de eventos para eventos universitários (formatura, colação, etc)
- Substitui horário único por horários de início e término obrigatórios
- Adiciona campo de curso relacionado ao evento
- Reorganiza ordem dos campos no formulário de instituição (CEP primeiro)
- Atualiza coordenadas padrão do mapa para Americana-SP
- Melhora layout do mapa: cards de coordenadas e instruções abaixo do mapa
2025-12-03 14:51:15 -03:00

231 lines
7.3 KiB
TypeScript

import React, { useState } from "react";
import { Institution, Address } from "../types";
import { Input, Select } from "./Input";
import { Button } from "./Button";
import { Building2, X, Check } from "lucide-react";
interface InstitutionFormProps {
onCancel: () => void;
onSubmit: (data: Partial<Institution>) => void;
initialData?: Institution;
userId: string;
}
const INSTITUTION_TYPES = [
"Universidade Pública",
"Universidade Privada",
"Faculdade",
"Instituto Federal",
"Centro Universitário",
"Campus Universitário",
];
export const InstitutionForm: React.FC<InstitutionFormProps> = ({
onCancel,
onSubmit,
initialData,
userId,
}) => {
const [formData, setFormData] = useState<Partial<Institution>>(
initialData || {
name: "",
type: "",
cnpj: "",
phone: "",
email: "",
description: "",
ownerId: userId,
address: {
street: "",
number: "",
city: "",
state: "",
zip: "",
},
}
);
const [showToast, setShowToast] = useState(false);
const [stateError, setStateError] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setShowToast(true);
setTimeout(() => {
onSubmit(formData);
}, 1000);
};
const handleChange = (field: keyof Institution, value: any) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
const handleAddressChange = (field: keyof Address, value: string) => {
setFormData((prev) => ({
...prev,
address: {
...prev.address!,
[field]: value,
},
}));
};
return (
<div className="bg-white rounded-lg shadow-xl overflow-hidden max-w-2xl mx-auto border border-gray-100 slide-up relative">
{/* Success Toast */}
{showToast && (
<div className="absolute top-4 right-4 z-50 bg-brand-black text-white px-6 py-4 rounded shadow-2xl flex items-center space-x-3 fade-in">
<Check className="text-brand-gold h-6 w-6" />
<div>
<h4 className="font-bold text-sm">Sucesso!</h4>
<p className="text-xs text-gray-300">
Universidade cadastrada com sucesso.
</p>
</div>
</div>
)}
{/* Form Header */}
<div className="bg-gray-50 border-b px-8 py-6 flex justify-between items-center">
<div className="flex items-center space-x-3">
<Building2 className="text-brand-gold h-8 w-8" />
<div>
<h2 className="text-2xl font-serif text-brand-black">
{initialData ? "Editar Universidade" : "Cadastrar Universidade"}
</h2>
<p className="text-sm text-gray-500 mt-1">
Registre a universidade onde os eventos fotográficos serão
realizados
</p>
</div>
</div>
<button
onClick={onCancel}
className="p-2 hover:bg-gray-200 rounded-full transition-colors"
>
<X size={20} className="text-gray-600" />
</button>
</div>
<form onSubmit={handleSubmit} className="p-8 space-y-6">
{/* Informações Básicas */}
<div className="space-y-4">
<h3 className="text-sm font-semibold text-gray-700 tracking-wide uppercase">
Informações Básicas
</h3>
<Input
label="Nome da Universidade*"
placeholder="Ex: Universidade Federal do Rio Grande do Sul"
value={formData.name || ""}
onChange={(e) => handleChange("name", e.target.value)}
required
/>
<div className="grid grid-cols-2 gap-4">
<Select
label="Tipo de Universidade*"
options={INSTITUTION_TYPES.map((t) => ({ value: t, label: t }))}
value={formData.type || ""}
onChange={(e) => handleChange("type", e.target.value)}
required
/>
<Input
label="CNPJ (Opcional)"
placeholder="00.000.000/0000-00"
value={formData.cnpj || ""}
onChange={(e) => handleChange("cnpj", e.target.value)}
mask="cnpj"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
Descrição (Opcional)
</label>
<textarea
className="w-full border border-gray-300 rounded-sm p-3 focus:outline-none focus:border-brand-gold h-24 text-sm"
placeholder="Ex: Campus principal, informações sobre o campus, áreas para eventos..."
value={formData.description || ""}
onChange={(e) => handleChange("description", e.target.value)}
/>
</div>
</div>
{/* Endereço */}
<div className="space-y-4 border-t pt-6">
<h3 className="text-sm font-semibold text-gray-700 tracking-wide uppercase">
Endereço (Opcional)
</h3>
<Input
label="CEP"
placeholder="00000-000"
value={formData.address?.zip || ""}
onChange={(e) => handleAddressChange("zip", e.target.value)}
mask="cep"
/>
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2">
<Input
label="Rua"
placeholder="Nome da rua"
value={formData.address?.street || ""}
onChange={(e) => handleAddressChange("street", e.target.value)}
/>
</div>
<Input
label="Número"
placeholder="123"
value={formData.address?.number || ""}
onChange={(e) => {
const value = e.target.value.replace(/\D/g, "");
handleAddressChange("number", value);
}}
type="text"
inputMode="numeric"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<Input
label="Cidade"
placeholder="Cidade"
value={formData.address?.city || ""}
onChange={(e) => handleAddressChange("city", e.target.value)}
/>
<Input
label="Estado"
placeholder="UF"
value={formData.address?.state || ""}
onChange={(e) => {
const hasNumbers = /[0-9]/.test(e.target.value);
if (hasNumbers) {
setStateError("O campo Estado aceita apenas letras");
setTimeout(() => setStateError(""), 3000);
}
const value = e.target.value
.replace(/[0-9]/g, "")
.toUpperCase();
handleAddressChange("state", value);
}}
maxLength={2}
error={stateError}
/>
</div>
</div>
{/* Actions */}
<div className="flex justify-end space-x-3 pt-6 border-t">
<Button variant="outline" onClick={onCancel} type="button">
Cancelar
</Button>
<Button type="submit" variant="secondary">
{initialData ? "Salvar Alterações" : "Cadastrar Universidade"}
</Button>
</div>
</form>
</div>
);
};