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
This commit is contained in:
parent
31325d50eb
commit
ac274e5c91
4 changed files with 187 additions and 171 deletions
|
|
@ -63,6 +63,8 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
name: "",
|
name: "",
|
||||||
date: "",
|
date: "",
|
||||||
time: "",
|
time: "",
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
type: "",
|
type: "",
|
||||||
status: EventStatus.PLANNING,
|
status: EventStatus.PLANNING,
|
||||||
address: {
|
address: {
|
||||||
|
|
@ -71,8 +73,8 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
city: "",
|
city: "",
|
||||||
state: "",
|
state: "",
|
||||||
zip: "",
|
zip: "",
|
||||||
lat: -23.5505,
|
lat: -22.7394,
|
||||||
lng: -46.6333,
|
lng: -47.3314,
|
||||||
mapLink: "",
|
mapLink: "",
|
||||||
} as Address,
|
} as Address,
|
||||||
briefing: "",
|
briefing: "",
|
||||||
|
|
@ -81,6 +83,8 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
coverImage:
|
coverImage:
|
||||||
"https://images.unsplash.com/photo-1511795409834-ef04bbd61622?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80", // Default
|
"https://images.unsplash.com/photo-1511795409834-ef04bbd61622?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80", // Default
|
||||||
institutionId: "",
|
institutionId: "",
|
||||||
|
attendees: "",
|
||||||
|
course: "",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -336,30 +340,42 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
<div className="grid grid-cols-1 gap-6">
|
<div className="grid grid-cols-1 gap-6">
|
||||||
<Input
|
<Input
|
||||||
label="Nome do Evento (Opcional)"
|
label="Nome do Evento (Opcional)"
|
||||||
placeholder="Ex: Casamento Silva & Souza"
|
placeholder="Ex: Formatura Educação Física 2025"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, name: e.target.value })
|
setFormData({ ...formData, name: e.target.value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Input
|
||||||
|
label="Data Pretendida"
|
||||||
|
type="date"
|
||||||
|
value={formData.date}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({ ...formData, date: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<Input
|
<Input
|
||||||
label="Data Pretendida"
|
label="Horário de Início*"
|
||||||
type="date"
|
type="time"
|
||||||
value={formData.date}
|
value={formData.startTime}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, date: e.target.value })
|
setFormData({ ...formData, startTime: e.target.value })
|
||||||
}
|
}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="Horário Aproximado"
|
label="Horário de Término*"
|
||||||
type="time"
|
type="time"
|
||||||
value={formData.time}
|
value={formData.endTime}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, time: e.target.value })
|
setFormData({ ...formData, endTime: e.target.value })
|
||||||
}
|
}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
label="Tipo de Evento"
|
label="Tipo de Evento"
|
||||||
options={Object.values(EventType).map((t) => ({
|
options={Object.values(EventType).map((t) => ({
|
||||||
|
|
@ -372,6 +388,30 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="Curso"
|
||||||
|
placeholder="Ex: Engenharia Civil, Medicina, Direito"
|
||||||
|
value={formData.course}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({ ...formData, course: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="Número de Pessoas"
|
||||||
|
placeholder="Ex: 150"
|
||||||
|
value={formData.attendees}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
// Permite apenas números
|
||||||
|
if (value === "" || /^\d+$/.test(value)) {
|
||||||
|
setFormData({ ...formData, attendees: value });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Institution Selection - OBRIGATÓRIO */}
|
{/* Institution Selection - OBRIGATÓRIO */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
||||||
|
|
@ -640,8 +680,8 @@ export const EventForm: React.FC<EventFormProps> = ({
|
||||||
)}
|
)}
|
||||||
</label>
|
</label>
|
||||||
<MapboxMap
|
<MapboxMap
|
||||||
initialLat={formData.address.lat || -23.5505}
|
initialLat={formData.address.lat || -22.7394}
|
||||||
initialLng={formData.address.lng || -46.6333}
|
initialLng={formData.address.lng || -47.3314}
|
||||||
onLocationChange={handleMapLocationChange}
|
onLocationChange={handleMapLocationChange}
|
||||||
height="450px"
|
height="450px"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
import React, { useState } from 'react';
|
import { Institution, Address } from "../types";
|
||||||
import { Institution, Address } from '../types';
|
import { Input, Select } from "./Input";
|
||||||
import { Input, Select } from './Input';
|
import { Button } from "./Button";
|
||||||
import { Button } from './Button';
|
import { Building2, X, Check } from "lucide-react";
|
||||||
import { Building2, X, Check } from 'lucide-react';
|
|
||||||
|
|
||||||
interface InstitutionFormProps {
|
interface InstitutionFormProps {
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
|
@ -13,39 +12,41 @@ interface InstitutionFormProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const INSTITUTION_TYPES = [
|
const INSTITUTION_TYPES = [
|
||||||
'Universidade Pública',
|
"Universidade Pública",
|
||||||
'Universidade Privada',
|
"Universidade Privada",
|
||||||
'Faculdade',
|
"Faculdade",
|
||||||
'Instituto Federal',
|
"Instituto Federal",
|
||||||
'Centro Universitário',
|
"Centro Universitário",
|
||||||
'Campus Universitário'
|
"Campus Universitário",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
onCancel,
|
onCancel,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
initialData,
|
initialData,
|
||||||
userId
|
userId,
|
||||||
}) => {
|
}) => {
|
||||||
const [formData, setFormData] = useState<Partial<Institution>>(initialData || {
|
const [formData, setFormData] = useState<Partial<Institution>>(
|
||||||
name: '',
|
initialData || {
|
||||||
type: '',
|
name: "",
|
||||||
cnpj: '',
|
type: "",
|
||||||
phone: '',
|
cnpj: "",
|
||||||
email: '',
|
phone: "",
|
||||||
description: '',
|
email: "",
|
||||||
ownerId: userId,
|
description: "",
|
||||||
address: {
|
ownerId: userId,
|
||||||
street: '',
|
address: {
|
||||||
number: '',
|
street: "",
|
||||||
city: '',
|
number: "",
|
||||||
state: '',
|
city: "",
|
||||||
zip: ''
|
state: "",
|
||||||
|
zip: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const [showToast, setShowToast] = useState(false);
|
const [showToast, setShowToast] = useState(false);
|
||||||
const [stateError, setStateError] = useState('');
|
const [stateError, setStateError] = useState("");
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -56,29 +57,30 @@ export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (field: keyof Institution, value: any) => {
|
const handleChange = (field: keyof Institution, value: any) => {
|
||||||
setFormData(prev => ({ ...prev, [field]: value }));
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddressChange = (field: keyof Address, value: string) => {
|
const handleAddressChange = (field: keyof Address, value: string) => {
|
||||||
setFormData(prev => ({
|
setFormData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
address: {
|
address: {
|
||||||
...prev.address!,
|
...prev.address!,
|
||||||
[field]: value
|
[field]: value,
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow-xl overflow-hidden max-w-2xl mx-auto border border-gray-100 slide-up relative">
|
<div className="bg-white rounded-lg shadow-xl overflow-hidden max-w-2xl mx-auto border border-gray-100 slide-up relative">
|
||||||
|
|
||||||
{/* Success Toast */}
|
{/* Success Toast */}
|
||||||
{showToast && (
|
{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">
|
<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" />
|
<Check className="text-brand-gold h-6 w-6" />
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-bold text-sm">Sucesso!</h4>
|
<h4 className="font-bold text-sm">Sucesso!</h4>
|
||||||
<p className="text-xs text-gray-300">Universidade cadastrada com sucesso.</p>
|
<p className="text-xs text-gray-300">
|
||||||
|
Universidade cadastrada com sucesso.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -89,10 +91,11 @@ export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
<Building2 className="text-brand-gold h-8 w-8" />
|
<Building2 className="text-brand-gold h-8 w-8" />
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-serif text-brand-black">
|
<h2 className="text-2xl font-serif text-brand-black">
|
||||||
{initialData ? 'Editar Universidade' : 'Cadastrar Universidade'}
|
{initialData ? "Editar Universidade" : "Cadastrar Universidade"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
<p className="text-sm text-gray-500 mt-1">
|
||||||
Registre a universidade onde os eventos fotográficos serão realizados
|
Registre a universidade onde os eventos fotográficos serão
|
||||||
|
realizados
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -105,60 +108,37 @@ export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="p-8 space-y-6">
|
<form onSubmit={handleSubmit} className="p-8 space-y-6">
|
||||||
|
|
||||||
{/* Informações Básicas */}
|
{/* Informações Básicas */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-sm font-semibold text-gray-700 tracking-wide uppercase">
|
<h3 className="text-sm font-semibold text-gray-700 tracking-wide uppercase">
|
||||||
Informações Básicas
|
Informações Básicas
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label="Nome da Universidade*"
|
label="Nome da Universidade*"
|
||||||
placeholder="Ex: Universidade Federal do Rio Grande do Sul"
|
placeholder="Ex: Universidade Federal do Rio Grande do Sul"
|
||||||
value={formData.name || ''}
|
value={formData.name || ""}
|
||||||
onChange={(e) => handleChange('name', e.target.value)}
|
onChange={(e) => handleChange("name", e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<Select
|
<Select
|
||||||
label="Tipo de Universidade*"
|
label="Tipo de Universidade*"
|
||||||
options={INSTITUTION_TYPES.map(t => ({ value: t, label: t }))}
|
options={INSTITUTION_TYPES.map((t) => ({ value: t, label: t }))}
|
||||||
value={formData.type || ''}
|
value={formData.type || ""}
|
||||||
onChange={(e) => handleChange('type', e.target.value)}
|
onChange={(e) => handleChange("type", e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label="CNPJ (Opcional)"
|
label="CNPJ (Opcional)"
|
||||||
placeholder="00.000.000/0000-00"
|
placeholder="00.000.000/0000-00"
|
||||||
value={formData.cnpj || ''}
|
value={formData.cnpj || ""}
|
||||||
onChange={(e) => handleChange('cnpj', e.target.value)}
|
onChange={(e) => handleChange("cnpj", e.target.value)}
|
||||||
mask="cnpj"
|
mask="cnpj"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<Input
|
|
||||||
label="Telefone*"
|
|
||||||
type="tel"
|
|
||||||
placeholder="(00) 00000-0000"
|
|
||||||
value={formData.phone || ''}
|
|
||||||
onChange={(e) => handleChange('phone', e.target.value)}
|
|
||||||
mask="phone"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="E-mail*"
|
|
||||||
type="email"
|
|
||||||
placeholder="contato@instituicao.com"
|
|
||||||
value={formData.email || ''}
|
|
||||||
onChange={(e) => handleChange('email', e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
<label className="block text-sm font-medium text-gray-700 mb-1 tracking-wide uppercase text-xs">
|
||||||
Descrição (Opcional)
|
Descrição (Opcional)
|
||||||
|
|
@ -166,8 +146,8 @@ export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
<textarea
|
<textarea
|
||||||
className="w-full border border-gray-300 rounded-sm p-3 focus:outline-none focus:border-brand-gold h-24 text-sm"
|
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..."
|
placeholder="Ex: Campus principal, informações sobre o campus, áreas para eventos..."
|
||||||
value={formData.description || ''}
|
value={formData.description || ""}
|
||||||
onChange={(e) => handleChange('description', e.target.value)}
|
onChange={(e) => handleChange("description", e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -177,59 +157,62 @@ export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
<h3 className="text-sm font-semibold text-gray-700 tracking-wide uppercase">
|
<h3 className="text-sm font-semibold text-gray-700 tracking-wide uppercase">
|
||||||
Endereço (Opcional)
|
Endereço (Opcional)
|
||||||
</h3>
|
</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="grid grid-cols-3 gap-4">
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<Input
|
<Input
|
||||||
label="Rua"
|
label="Rua"
|
||||||
placeholder="Nome da rua"
|
placeholder="Nome da rua"
|
||||||
value={formData.address?.street || ''}
|
value={formData.address?.street || ""}
|
||||||
onChange={(e) => handleAddressChange('street', e.target.value)}
|
onChange={(e) => handleAddressChange("street", e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
label="Número"
|
label="Número"
|
||||||
placeholder="123"
|
placeholder="123"
|
||||||
value={formData.address?.number || ''}
|
value={formData.address?.number || ""}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.target.value.replace(/\D/g, '');
|
const value = e.target.value.replace(/\D/g, "");
|
||||||
handleAddressChange('number', value);
|
handleAddressChange("number", value);
|
||||||
}}
|
}}
|
||||||
type="text"
|
type="text"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<Input
|
<Input
|
||||||
label="Cidade"
|
label="Cidade"
|
||||||
placeholder="Cidade"
|
placeholder="Cidade"
|
||||||
value={formData.address?.city || ''}
|
value={formData.address?.city || ""}
|
||||||
onChange={(e) => handleAddressChange('city', e.target.value)}
|
onChange={(e) => handleAddressChange("city", e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="Estado"
|
label="Estado"
|
||||||
placeholder="UF"
|
placeholder="UF"
|
||||||
value={formData.address?.state || ''}
|
value={formData.address?.state || ""}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const hasNumbers = /[0-9]/.test(e.target.value);
|
const hasNumbers = /[0-9]/.test(e.target.value);
|
||||||
if (hasNumbers) {
|
if (hasNumbers) {
|
||||||
setStateError('O campo Estado aceita apenas letras');
|
setStateError("O campo Estado aceita apenas letras");
|
||||||
setTimeout(() => setStateError(''), 3000);
|
setTimeout(() => setStateError(""), 3000);
|
||||||
}
|
}
|
||||||
const value = e.target.value.replace(/[0-9]/g, '').toUpperCase();
|
const value = e.target.value
|
||||||
handleAddressChange('state', value);
|
.replace(/[0-9]/g, "")
|
||||||
|
.toUpperCase();
|
||||||
|
handleAddressChange("state", value);
|
||||||
}}
|
}}
|
||||||
maxLength={2}
|
maxLength={2}
|
||||||
error={stateError}
|
error={stateError}
|
||||||
/>
|
/>
|
||||||
<Input
|
|
||||||
label="CEP"
|
|
||||||
placeholder="00000-000"
|
|
||||||
value={formData.address?.zip || ''}
|
|
||||||
onChange={(e) => handleAddressChange('zip', e.target.value)}
|
|
||||||
mask="cep"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -239,7 +222,7 @@ export const InstitutionForm: React.FC<InstitutionFormProps> = ({
|
||||||
Cancelar
|
Cancelar
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" variant="secondary">
|
<Button type="submit" variant="secondary">
|
||||||
{initialData ? 'Salvar Alterações' : 'Cadastrar Universidade'}
|
{initialData ? "Salvar Alterações" : "Cadastrar Universidade"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -153,72 +153,57 @@ export const MapboxMap: React.FC<MapboxMapProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="absolute top-0 left-0 right-0 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded z-10">
|
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-3">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div className="relative">
|
||||||
ref={mapContainer}
|
<div
|
||||||
className="w-full rounded-lg border-2 border-gray-300 overflow-hidden shadow-md"
|
ref={mapContainer}
|
||||||
style={{ height }}
|
className="w-full rounded-lg border-2 border-gray-300 overflow-hidden shadow-md"
|
||||||
/>
|
style={{ height }}
|
||||||
|
/>
|
||||||
{/* Info overlay - Responsivo */}
|
|
||||||
<div className="absolute bottom-2 sm:bottom-4 left-2 sm:left-4 bg-white/95 backdrop-blur-sm px-2 py-2 sm:px-4 sm:py-3 rounded-lg shadow-lg border border-gray-200 max-w-[160px] sm:max-w-none">
|
|
||||||
<div className="flex items-center space-x-2 sm:space-x-3">
|
|
||||||
<MapPin
|
|
||||||
size={16}
|
|
||||||
className="text-brand-gold flex-shrink-0 hidden sm:block"
|
|
||||||
/>
|
|
||||||
<div className="min-w-0">
|
|
||||||
<p className="text-[10px] sm:text-xs text-gray-500 font-medium hidden sm:block">
|
|
||||||
Coordenadas
|
|
||||||
</p>
|
|
||||||
<p className="text-[10px] sm:text-sm font-mono text-gray-800 truncate">
|
|
||||||
{currentLat.toFixed(4)}, {currentLng.toFixed(4)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Botão de centralizar - Responsivo */}
|
{/* Info cards abaixo do mapa */}
|
||||||
<button
|
<div className="mt-3 grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
onClick={centerOnMarker}
|
{/* Card de Coordenadas */}
|
||||||
className="absolute bottom-2 sm:bottom-4 right-2 sm:right-4 bg-white hover:bg-gray-50 p-2 sm:p-3 rounded-full shadow-lg border border-gray-200 transition-colors group"
|
<div className="bg-white rounded-lg shadow border border-gray-200 p-3">
|
||||||
title="Centralizar no marcador"
|
<div className="flex items-center space-x-2">
|
||||||
>
|
<MapPin size={16} className="text-brand-gold flex-shrink-0" />
|
||||||
<Target
|
<div className="min-w-0 flex-1">
|
||||||
size={18}
|
<p className="text-xs text-gray-500 font-medium">Coordenadas</p>
|
||||||
className="text-gray-600 group-hover:text-brand-gold transition-colors sm:w-5 sm:h-5"
|
<p className="text-sm font-mono text-gray-800 truncate">
|
||||||
/>
|
{currentLat.toFixed(4)}, {currentLng.toFixed(4)}
|
||||||
</button>
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Instruções - Responsivo */}
|
{/* Card de Instruções */}
|
||||||
<div className="mt-3 bg-blue-50 border border-blue-200 rounded-lg p-2 sm:p-3 text-xs sm:text-sm text-blue-800">
|
<div className="bg-blue-50 rounded-lg shadow border border-blue-200 p-3">
|
||||||
<p className="font-medium mb-1 text-xs sm:text-sm">💡 Como usar:</p>
|
<p className="font-medium mb-1.5 text-xs text-blue-800">
|
||||||
<ul className="text-[11px] sm:text-xs space-y-0.5 sm:space-y-1 text-blue-700">
|
💡 Como usar:
|
||||||
<li className="flex items-start">
|
</p>
|
||||||
<span className="mr-1">•</span>
|
<ul className="text-xs space-y-1 text-blue-700">
|
||||||
<span>
|
<li className="flex items-start">
|
||||||
<strong>Arraste o marcador</strong> para a posição exata
|
<span className="mr-1">•</span>
|
||||||
</span>
|
<span>
|
||||||
</li>
|
<strong>Arraste o marcador</strong> ou{" "}
|
||||||
<li className="flex items-start">
|
<strong>clique no mapa</strong>
|
||||||
<span className="mr-1">•</span>
|
</span>
|
||||||
<span>
|
</li>
|
||||||
<strong>Clique no mapa</strong> para mover o marcador
|
<li className="flex items-start">
|
||||||
</span>
|
<span className="mr-1">•</span>
|
||||||
</li>
|
<span>
|
||||||
<li className="flex items-start hidden sm:flex">
|
Use os <strong>controles</strong> para navegação
|
||||||
<span className="mr-1">•</span>
|
</span>
|
||||||
<span>
|
</li>
|
||||||
Use os <strong>controles</strong> para zoom e navegação
|
</ul>
|
||||||
</span>
|
</div>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,14 @@ export enum EventStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
WEDDING = "Casamento",
|
GRADUATION = "Formatura",
|
||||||
CORPORATE = "Corporativo",
|
COLATION = "Colação de Grau",
|
||||||
BIRTHDAY = "Aniversário",
|
ACADEMIC_WEEK = "Semana Acadêmica",
|
||||||
DEBUTANTE = "Debutante",
|
FRESHMAN_WEEK = "Semana de Calouros",
|
||||||
|
SYMPOSIUM = "Simpósio/Congresso",
|
||||||
|
DEFENSE = "Defesa de TCC/Mestrado/Doutorado",
|
||||||
|
SPORTS_EVENT = "Evento Esportivo Universitário",
|
||||||
|
CULTURAL_EVENT = "Evento Cultural",
|
||||||
OTHER = "Outro",
|
OTHER = "Outro",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +77,9 @@ export interface EventData {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
date: string;
|
date: string;
|
||||||
time: string;
|
time: string; // Mantido por compatibilidade, mas deprecated
|
||||||
|
startTime?: string; // Horário de início
|
||||||
|
endTime?: string; // Horário de término
|
||||||
type: EventType;
|
type: EventType;
|
||||||
status: EventStatus;
|
status: EventStatus;
|
||||||
address: Address;
|
address: Address;
|
||||||
|
|
@ -84,4 +90,6 @@ export interface EventData {
|
||||||
ownerId: string; // ID do cliente dono do evento
|
ownerId: string; // ID do cliente dono do evento
|
||||||
photographerIds: string[]; // IDs dos fotógrafos designados
|
photographerIds: string[]; // IDs dos fotógrafos designados
|
||||||
institutionId?: string; // ID da instituição vinculada (obrigatório)
|
institutionId?: string; // ID da instituição vinculada (obrigatório)
|
||||||
|
attendees?: number; // Número de pessoas participantes
|
||||||
|
course?: string; // Curso relacionado ao evento
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue