223 lines
7.7 KiB
TypeScript
223 lines
7.7 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { Field, FieldError, FieldLabel } from '@/components/ui/field';
|
|
import { Input } from '@/components/ui/input';
|
|
import { apiClient } from '@/lib/api-client';
|
|
import { useAuth } from '@/lib/auth';
|
|
import { setCurrentComplexSlug } from '@/lib/current-complex';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
import type { PlanSummary } from '@repo/api-contract';
|
|
import { useNavigate } from '@tanstack/react-router';
|
|
import { useEffect, useState } from 'react';
|
|
import { useForm } from 'react-hook-form';
|
|
import { z } from 'zod';
|
|
|
|
const createComplexSchema = z.object({
|
|
complexName: z
|
|
.string()
|
|
.trim()
|
|
.min(3, 'El nombre del complejo debe tener al menos 3 caracteres.')
|
|
.max(120, 'El nombre del complejo no puede superar los 120 caracteres.'),
|
|
physicalAddress: z
|
|
.string()
|
|
.trim()
|
|
.min(5, 'La direccion debe tener al menos 5 caracteres.')
|
|
.max(200, 'La direccion no puede superar los 200 caracteres.'),
|
|
city: z.string().trim().max(100, 'La ciudad no puede superar los 100 caracteres.').optional(),
|
|
state: z
|
|
.string()
|
|
.trim()
|
|
.max(100, 'La provincia/estado no puede superar los 100 caracteres.')
|
|
.optional(),
|
|
country: z.string().trim().max(100, 'El país no puede superar los 100 caracteres.').optional(),
|
|
planCode: z
|
|
.string()
|
|
.trim()
|
|
.min(1, 'El planCode no puede estar vacío.')
|
|
.max(10, 'El planCode no puede superar los 10 caracteres.')
|
|
.optional(),
|
|
});
|
|
|
|
type CreateComplexForm = z.infer<typeof createComplexSchema>;
|
|
|
|
export function CreateComplexPage() {
|
|
const navigate = useNavigate();
|
|
const { user } = useAuth();
|
|
const [plans, setPlans] = useState<PlanSummary[]>([]);
|
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
const form = useForm<CreateComplexForm>({
|
|
resolver: zodResolver(createComplexSchema),
|
|
mode: 'onChange',
|
|
defaultValues: {
|
|
complexName: '',
|
|
physicalAddress: '',
|
|
city: '',
|
|
state: '',
|
|
country: '',
|
|
planCode: '',
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
void (async () => {
|
|
try {
|
|
const result = await apiClient.plans.list();
|
|
setPlans(result);
|
|
} catch {
|
|
setPlans([]);
|
|
}
|
|
})();
|
|
}, []);
|
|
|
|
const onSubmit = async (values: CreateComplexForm) => {
|
|
setErrorMessage(null);
|
|
setIsSubmitting(true);
|
|
|
|
try {
|
|
const pendingEmail = sessionStorage.getItem('pending-signup-email');
|
|
const payload: Parameters<typeof apiClient.complexes.create>[0] = {
|
|
complexName: values.complexName,
|
|
physicalAddress: values.physicalAddress,
|
|
city: values.city,
|
|
state: values.state,
|
|
country: values.country,
|
|
planCode: values.planCode,
|
|
};
|
|
|
|
if (pendingEmail) {
|
|
payload.adminEmail = pendingEmail;
|
|
}
|
|
|
|
const complex = await apiClient.complexes.create(payload);
|
|
|
|
sessionStorage.removeItem('pending-signup-email');
|
|
sessionStorage.removeItem('pending-signup-userId');
|
|
setCurrentComplexSlug(complex.complexSlug);
|
|
await navigate({
|
|
to: '/complex/$slug/edit',
|
|
params: { slug: complex.complexSlug },
|
|
});
|
|
} catch {
|
|
setErrorMessage('No pudimos crear el complejo. Intenta nuevamente.');
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<main className="flex min-h-screen w-full items-center justify-center px-6">
|
|
<section className="w-full max-w-sm rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
|
<h1 className="mb-2 text-2xl font-semibold">Crear tu complejo</h1>
|
|
<p className="mb-4 text-sm text-muted-foreground">
|
|
Completá los datos de tu complejo para comenzar.
|
|
</p>
|
|
|
|
<form className="space-y-3" onSubmit={form.handleSubmit(onSubmit)}>
|
|
{user?.email && (
|
|
<Field>
|
|
<FieldLabel htmlFor="user-email">Email</FieldLabel>
|
|
<Input id="user-email" type="email" value={user.email} disabled readOnly />
|
|
</Field>
|
|
)}
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.complexName)}>
|
|
<FieldLabel htmlFor="complexName">Nombre del complejo</FieldLabel>
|
|
<Input
|
|
id="complexName"
|
|
type="text"
|
|
placeholder="Complejo Las Palmeras"
|
|
aria-invalid={Boolean(form.formState.errors.complexName)}
|
|
{...form.register('complexName')}
|
|
/>
|
|
<FieldError errors={[form.formState.errors.complexName]} />
|
|
</Field>
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.physicalAddress)}>
|
|
<FieldLabel htmlFor="physicalAddress">Dirección física</FieldLabel>
|
|
<Input
|
|
id="physicalAddress"
|
|
type="text"
|
|
placeholder="Av. San Martin 1234, Salta"
|
|
aria-invalid={Boolean(form.formState.errors.physicalAddress)}
|
|
{...form.register('physicalAddress')}
|
|
/>
|
|
<FieldError errors={[form.formState.errors.physicalAddress]} />
|
|
</Field>
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.city)}>
|
|
<FieldLabel htmlFor="city">Ciudad</FieldLabel>
|
|
<Input
|
|
id="city"
|
|
type="text"
|
|
placeholder="Salta"
|
|
aria-invalid={Boolean(form.formState.errors.city)}
|
|
{...form.register('city')}
|
|
/>
|
|
<FieldError errors={[form.formState.errors.city]} />
|
|
</Field>
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.state)}>
|
|
<FieldLabel htmlFor="state">Provincia/Estado</FieldLabel>
|
|
<Input
|
|
id="state"
|
|
type="text"
|
|
placeholder="Salta"
|
|
aria-invalid={Boolean(form.formState.errors.state)}
|
|
{...form.register('state')}
|
|
/>
|
|
<FieldError errors={[form.formState.errors.state]} />
|
|
</Field>
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.country)}>
|
|
<FieldLabel htmlFor="country">País</FieldLabel>
|
|
<Input
|
|
id="country"
|
|
type="text"
|
|
placeholder="Argentina"
|
|
aria-invalid={Boolean(form.formState.errors.country)}
|
|
{...form.register('country')}
|
|
/>
|
|
<FieldError errors={[form.formState.errors.country]} />
|
|
</Field>
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.planCode)}>
|
|
<FieldLabel htmlFor="planCode">Plan</FieldLabel>
|
|
<select
|
|
id="planCode"
|
|
className="h-10 w-full rounded-md border bg-background px-3 text-sm"
|
|
aria-invalid={Boolean(form.formState.errors.planCode)}
|
|
{...form.register('planCode')}
|
|
>
|
|
<option value="">
|
|
{plans.length > 0 ? 'Selecciona un plan' : 'No hay planes disponibles'}
|
|
</option>
|
|
{plans.map((plan) => (
|
|
<option key={plan.code} value={plan.code}>
|
|
{plan.name} - ${plan.price.toFixed(2)}
|
|
</option>
|
|
))}
|
|
</select>
|
|
{plans.length === 0 && (
|
|
<p className="text-xs text-muted-foreground">
|
|
Carga planes en la base para continuar.
|
|
</p>
|
|
)}
|
|
<FieldError errors={[form.formState.errors.planCode]} />
|
|
</Field>
|
|
|
|
{errorMessage && <p className="text-sm text-destructive">{errorMessage}</p>}
|
|
|
|
<Button
|
|
type="submit"
|
|
className="w-full"
|
|
disabled={isSubmitting || !form.formState.isValid}
|
|
>
|
|
{isSubmitting ? 'Creando complejo...' : 'Crear complejo'}
|
|
</Button>
|
|
</form>
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|