210 lines
7.1 KiB
TypeScript
210 lines
7.1 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useFormStatus } from 'react-dom';
|
|
import { TIPO_LABEL } from '@/lib/funnel';
|
|
|
|
const TIPOS = ['cocina', 'bano', 'salon', 'comedor', 'integral', 'otro'] as const;
|
|
const CALIDADES = [
|
|
{ value: 'basica', label: 'Básica' },
|
|
{ value: 'media', label: 'Media' },
|
|
{ value: 'premium', label: 'Premium' },
|
|
] as const;
|
|
const URGENCIAS = [
|
|
{ value: 'alta', label: 'Cuanto antes' },
|
|
{ value: 'media', label: 'En unos meses' },
|
|
{ value: 'baja', label: 'Sin prisa' },
|
|
] as const;
|
|
|
|
const MAX_FOTOS = 4;
|
|
|
|
const inputClass =
|
|
'w-full px-4 py-3 text-base font-sans text-dark bg-white border-[1.5px] border-gray-200 rounded-lg transition-all duration-150 outline-none placeholder:text-gray-400 focus:border-black focus:shadow-[0_0_0_3px_rgba(0,0,0,0.06)]';
|
|
|
|
function SubmitButton({ disabled }: { disabled: boolean }) {
|
|
const { pending } = useFormStatus();
|
|
return (
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary btn-lg w-full justify-center mt-1 disabled:opacity-60 disabled:cursor-not-allowed disabled:transform-none"
|
|
disabled={pending || disabled}
|
|
aria-busy={pending}
|
|
>
|
|
{pending ? (
|
|
<>
|
|
<span
|
|
className="w-[18px] h-[18px] border-2 border-white/30 border-t-white rounded-full animate-[spin_0.7s_linear_infinite] shrink-0"
|
|
aria-hidden="true"
|
|
/>
|
|
Generando tu presupuesto...
|
|
</>
|
|
) : (
|
|
'Generar mi presupuesto'
|
|
)}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
export default function FotosUploader({
|
|
action,
|
|
}: {
|
|
action: (formData: FormData) => void | Promise<void>;
|
|
}) {
|
|
const [previews, setPreviews] = useState<string[]>([]);
|
|
|
|
const handleFiles = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const files = Array.from(e.target.files ?? []).slice(0, MAX_FOTOS);
|
|
previews.forEach((url) => URL.revokeObjectURL(url));
|
|
setPreviews(files.map((f) => URL.createObjectURL(f)));
|
|
};
|
|
|
|
return (
|
|
<form action={action} className="flex flex-col gap-5">
|
|
{/* Fotos */}
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="fotos" className="text-sm font-semibold text-dark">
|
|
Sube fotos del espacio <span className="text-gray-400 font-normal">(hasta {MAX_FOTOS})</span>
|
|
</label>
|
|
<input
|
|
id="fotos"
|
|
name="fotos"
|
|
type="file"
|
|
accept="image/*"
|
|
multiple
|
|
onChange={handleFiles}
|
|
className="block w-full text-sm text-gray-600 file:mr-4 file:py-2.5 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-black file:text-white hover:file:bg-gray-800 file:cursor-pointer cursor-pointer"
|
|
/>
|
|
{previews.length > 0 && (
|
|
<div className="flex flex-wrap gap-3 mt-2">
|
|
{previews.map((url, i) => (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
key={i}
|
|
src={url}
|
|
alt=""
|
|
className="w-20 h-20 object-cover rounded-lg border border-gray-200"
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Tipo de reforma */}
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="tipoReforma" className="text-sm font-semibold text-dark">
|
|
¿Qué quieres reformar?
|
|
</label>
|
|
<select id="tipoReforma" name="tipoReforma" defaultValue="cocina" className={inputClass}>
|
|
{TIPOS.map((t) => (
|
|
<option key={t} value={t}>
|
|
{TIPO_LABEL[t]}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* m2 + calidad */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="m2" className="text-sm font-semibold text-dark">
|
|
Metros cuadrados <span className="text-gray-400 font-normal">(aprox.)</span>
|
|
</label>
|
|
<input
|
|
id="m2"
|
|
name="m2"
|
|
type="number"
|
|
min="1"
|
|
step="1"
|
|
inputMode="numeric"
|
|
placeholder="12"
|
|
className={inputClass}
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="calidad" className="text-sm font-semibold text-dark">
|
|
Nivel de acabado
|
|
</label>
|
|
<select id="calidad" name="calidad" defaultValue="media" className={inputClass}>
|
|
{CALIDADES.map((c) => (
|
|
<option key={c.value} value={c.value}>
|
|
{c.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Provincia */}
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="provincia" className="text-sm font-semibold text-dark">
|
|
Provincia
|
|
</label>
|
|
<input
|
|
id="provincia"
|
|
name="provincia"
|
|
type="text"
|
|
placeholder="Madrid"
|
|
autoComplete="address-level1"
|
|
className={inputClass}
|
|
/>
|
|
</div>
|
|
|
|
{/* Urgencia + presupuesto objetivo */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="urgencia" className="text-sm font-semibold text-dark">
|
|
¿Para cuándo?
|
|
</label>
|
|
<select id="urgencia" name="urgencia" defaultValue="media" className={inputClass}>
|
|
{URGENCIAS.map((u) => (
|
|
<option key={u.value} value={u.value}>
|
|
{u.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="presupuestoTarget" className="text-sm font-semibold text-dark">
|
|
Presupuesto objetivo <span className="text-gray-400 font-normal">(opcional, €)</span>
|
|
</label>
|
|
<input
|
|
id="presupuestoTarget"
|
|
name="presupuestoTarget"
|
|
type="number"
|
|
min="0"
|
|
step="100"
|
|
inputMode="numeric"
|
|
placeholder="8000"
|
|
className={inputClass}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Cambios estructurales */}
|
|
<label className="flex items-center gap-3 text-sm font-medium text-dark cursor-pointer">
|
|
<input type="checkbox" name="estructural" className="w-4 h-4 accent-black" />
|
|
Hay que mover sanitarios, tirar algún muro o cambiar la distribución
|
|
</label>
|
|
|
|
{/* Bloque abierto de gustos */}
|
|
<div className="flex flex-col gap-2">
|
|
<label htmlFor="tasteText" className="text-sm font-semibold text-dark">
|
|
Cuéntanos cómo lo imaginas
|
|
</label>
|
|
<textarea
|
|
id="tasteText"
|
|
name="tasteText"
|
|
rows={4}
|
|
placeholder="Estilo, colores, materiales que te gusten… y cualquier capricho que no quieras que falte (una isla, ducha de obra, encimera de cuarzo…)."
|
|
className={inputClass}
|
|
/>
|
|
</div>
|
|
|
|
<SubmitButton disabled={false} />
|
|
<p className="text-xs text-gray-400 text-center">
|
|
Calculamos un presupuesto orientativo con tus datos. Sin compromiso.
|
|
</p>
|
|
</form>
|
|
);
|
|
}
|