2 Commits

9 changed files with 34 additions and 15 deletions

View File

@@ -298,7 +298,7 @@ export async function createAdminRecurringBooking(
customerEmail: b.customerEmail, customerEmail: b.customerEmail,
price: 0, price: 0,
status: b.status as 'CONFIRMED', status: b.status as 'CONFIRMED',
recurringGroupId: groupId, recurringGroupId: result.group.id,
createdAt: b.createdAt.toISOString(), createdAt: b.createdAt.toISOString(),
updatedAt: b.updatedAt.toISOString(), updatedAt: b.updatedAt.toISOString(),
})), })),

View File

@@ -113,7 +113,7 @@ export async function ensureComplexAccess(
Result<{ Result<{
id: string; id: string;
complexName: string; complexName: string;
plan: { rules: string } | null; plan: { rules: unknown } | null;
}> }>
> { > {
const complexUser = await db.complexUser.findUnique({ const complexUser = await db.complexUser.findUnique({

View File

@@ -1,6 +1,5 @@
import { db } from '@/lib/prisma'; import { db } from '@/lib/prisma';
import { buildUniqueSlug, slugify } from '@/lib/slug'; import { buildUniqueSlug, slugify } from '@/lib/slug';
import { ensureActiveSport } from '@/modules/court/shared/guards';
import type { CreateComplexInput } from '@repo/api-contract'; import type { CreateComplexInput } from '@repo/api-contract';
import { v7 as uuidv7 } from 'uuid'; import { v7 as uuidv7 } from 'uuid';
@@ -65,7 +64,15 @@ export async function createComplex(input: CreateComplexInternalInput) {
} }
if (input.setupCourts && input.courtSportId) { if (input.setupCourts && input.courtSportId) {
const sport = await ensureActiveSport(input.courtSportId); const sport = await tx.sport.findFirst({
where: { id: input.courtSportId, isActive: true },
select: { name: true },
});
if (!sport) {
throw new Error('El deporte seleccionado no existe o esta inactivo.');
}
const availability = (input.courtDaysOfWeek ?? []).map((day) => ({ const availability = (input.courtDaysOfWeek ?? []).map((day) => ({
dayOfWeek: day as DayOfWeek, dayOfWeek: day as DayOfWeek,
startTime: input.courtStartTime ?? '08:00', startTime: input.courtStartTime ?? '08:00',

View File

@@ -34,7 +34,11 @@ export type PriceableCourt = {
}>; }>;
}; };
export type ComplexWithPublicBookingData = Awaited<ReturnType<typeof getComplexWithBookingData>>; export type ComplexWithPublicBookingData = {
courts: Array<{
sport: { id: string; name: string; slug: string; isActive: boolean };
}>;
};
export function toMinutes(value: string): number { export function toMinutes(value: string): number {
const [hours, minutes] = value.split(':').map((part) => Number(part)); const [hours, minutes] = value.split(':').map((part) => Number(part));
@@ -171,7 +175,7 @@ export function validateSportSelection(
export async function getComplexWithBookingData( export async function getComplexWithBookingData(
complexSlug: string complexSlug: string
): Promise<Result<ComplexWithPublicBookingData>> { ) {
const complex = await db.complex.findUnique({ const complex = await db.complex.findUnique({
where: { complexSlug }, where: { complexSlug },
select: { select: {

View File

@@ -1,10 +1,16 @@
import { beforeEach, expect, mock, test } from 'bun:test'; import { beforeEach, expect, mock, test } from 'bun:test';
import { Errors } from '@/lib/errors'; import { Errors } from '@/lib/errors';
import { err, ok } from '@/lib/result'; import { type Result, err, ok } from '@/lib/result';
import type { InviteComplexUserResponse } from '@repo/api-contract'; import type { InviteComplexUserResponse } from '@repo/api-contract';
const inviteComplexUserMock = mock(async () => undefined as unknown as InviteComplexUserResponse); type InviteComplexUserFn = (
userId: string,
complexId: string,
input: { email: string }
) => Promise<Result<InviteComplexUserResponse>>;
const inviteComplexUserMock = mock<InviteComplexUserFn>(async () => undefined as never);
const { createInviteComplexUserHandler } = await import( const { createInviteComplexUserHandler } = await import(
'@/modules/complex/features/invite-complex-user/invite-complex-user.handler' '@/modules/complex/features/invite-complex-user/invite-complex-user.handler'

View File

@@ -10,8 +10,8 @@ const createSportMock = mock(async (_input: CreateSportInput) =>
name: 'Tenis', name: 'Tenis',
slug: 'tenis', slug: 'tenis',
isActive: true, isActive: true,
createdAt: '2026-04-23T00:00:00.000Z', createdAt: new Date('2026-04-23T00:00:00.000Z'),
updatedAt: '2026-04-23T00:00:00.000Z', updatedAt: new Date('2026-04-23T00:00:00.000Z'),
}) })
); );
@@ -56,8 +56,8 @@ test('returns 201 with the created sport', async () => {
name: 'Tenis', name: 'Tenis',
slug: 'tenis', slug: 'tenis',
isActive: true, isActive: true,
createdAt: '2026-04-23T00:00:00.000Z', createdAt: new Date('2026-04-23T00:00:00.000Z'),
updatedAt: '2026-04-23T00:00:00.000Z', updatedAt: new Date('2026-04-23T00:00:00.000Z'),
} as const; } as const;
createSportMock.mockResolvedValue(ok(createdSport)); createSportMock.mockResolvedValue(ok(createdSport));

View File

@@ -15,8 +15,10 @@
"./src/*" "./src/*"
] ]
}, },
"rootDir": "./src",
"outDir": "./dist", "outDir": "./dist",
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "hono/jsx" "jsxImportSource": "hono/jsx"
} },
"include": ["src"]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +1,4 @@
import PlayzerLogo from '@/assets/playzer-logo-transparent.svg'; import PlayzerLogo from '@/assets/playzer-logo-transparent.webp';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { import {