feat: add recurring bookings feature with related handlers and services

- Introduced recurring booking groups with the ability to create and cancel them.
- Added database migrations for recurring bookings, including new tables and relationships.
- Implemented handlers for creating and canceling recurring bookings in the admin booking module.
- Enhanced existing booking services to support recurring bookings logic.
- Updated API contract to include new schemas for recurring bookings.
- Refactored existing code for improved readability and maintainability.
This commit is contained in:
Jose Selesan
2026-06-16 11:58:06 -03:00
parent 1b5eb253f2
commit 1318e3bf57
38 changed files with 2025 additions and 265 deletions

View File

@@ -14,6 +14,7 @@ model Complex {
users ComplexUser[]
invitations ComplexInvitation[]
courts Court[]
recurringGroups RecurringBookingGroup[]
@@index([planCode])
@@index([complexSlug])

View File

@@ -15,6 +15,11 @@ enum CourtBookingStatus {
NOSHOW
}
enum RecurringBookingGroupStatus {
ACTIVE
CANCELLED
}
model Sport {
id String @id @db.Uuid
name String @unique
@@ -44,6 +49,7 @@ model Court {
priceRules CourtPriceRule[]
bookings CourtBooking[]
maintenances CourtMaintenance[]
recurringGroups RecurringBookingGroup[]
@@index([complexId])
@@index([sportId])
@@ -95,23 +101,26 @@ model CourtPriceRule {
}
model CourtBooking {
id String @id @db.Uuid
bookingCode String @unique @map("booking_code") @db.VarChar(8)
courtId String @map("court_id") @db.Uuid
bookingDate DateTime @map("booking_date") @db.Date
startTime String @map("start_time") @db.VarChar(5)
endTime String @map("end_time") @db.VarChar(5)
customerName String @map("customer_name") @db.VarChar(120)
customerPhone String @map("customer_phone") @db.VarChar(30)
customerEmail String @map("customer_email") @db.VarChar(254)
status CourtBookingStatus @default(CONFIRMED)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
id String @id @db.Uuid
bookingCode String @unique @map("booking_code") @db.VarChar(8)
courtId String @map("court_id") @db.Uuid
bookingDate DateTime @map("booking_date") @db.Date
startTime String @map("start_time") @db.VarChar(5)
endTime String @map("end_time") @db.VarChar(5)
customerName String @map("customer_name") @db.VarChar(120)
customerPhone String @map("customer_phone") @db.VarChar(30)
customerEmail String @map("customer_email") @db.VarChar(254)
status CourtBookingStatus @default(CONFIRMED)
recurringGroupId String? @map("recurring_group_id") @db.Uuid
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
recurringGroup RecurringBookingGroup? @relation(fields: [recurringGroupId], references: [id])
@@unique([courtId, bookingDate, startTime])
@@index([courtId, bookingDate])
@@index([bookingDate])
@@index([recurringGroupId])
@@map("court_bookings")
}
@@ -132,3 +141,28 @@ model CourtBookingLog {
@@map("court_booking_logs")
@@index([courtId, newStatus])
}
model RecurringBookingGroup {
id String @id @db.Uuid
complexId String @map("complex_id") @db.Uuid
courtId String @map("court_id") @db.Uuid
startTime String @map("start_time") @db.VarChar(5)
endTime String @map("end_time") @db.VarChar(5)
dayOfWeek DayOfWeek @map("day_of_week")
startDate DateTime @map("start_date") @db.Date
endDate DateTime? @map("end_date") @db.Date
status RecurringBookingGroupStatus @default(ACTIVE)
customerName String @map("customer_name") @db.VarChar(120)
customerPhone String @map("customer_phone") @db.VarChar(30)
customerEmail String @map("customer_email") @db.VarChar(254)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
bookings CourtBooking[]
@@index([complexId])
@@index([courtId])
@@index([status])
@@map("recurring_booking_groups")
}

View File

@@ -0,0 +1,46 @@
-- CreateEnum
CREATE TYPE "RecurringBookingGroupStatus" AS ENUM ('ACTIVE', 'CANCELLED');
-- AlterTable
ALTER TABLE "court_bookings" ADD COLUMN "recurring_group_id" UUID;
-- CreateTable
CREATE TABLE "recurring_booking_groups" (
"id" UUID NOT NULL,
"complex_id" UUID NOT NULL,
"court_id" UUID NOT NULL,
"start_time" VARCHAR(5) NOT NULL,
"end_time" VARCHAR(5) NOT NULL,
"day_of_week" "DayOfWeek" NOT NULL,
"start_date" DATE NOT NULL,
"end_date" DATE,
"status" "RecurringBookingGroupStatus" NOT NULL DEFAULT 'ACTIVE',
"customer_name" VARCHAR(120) NOT NULL,
"customer_phone" VARCHAR(30) NOT NULL,
"customer_email" VARCHAR(254) NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "recurring_booking_groups_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "recurring_booking_groups_complex_id_idx" ON "recurring_booking_groups"("complex_id");
-- CreateIndex
CREATE INDEX "recurring_booking_groups_court_id_idx" ON "recurring_booking_groups"("court_id");
-- CreateIndex
CREATE INDEX "recurring_booking_groups_status_idx" ON "recurring_booking_groups"("status");
-- CreateIndex
CREATE INDEX "court_bookings_recurring_group_id_idx" ON "court_bookings"("recurring_group_id");
-- AddForeignKey
ALTER TABLE "court_bookings" ADD CONSTRAINT "court_bookings_recurring_group_id_fkey" FOREIGN KEY ("recurring_group_id") REFERENCES "recurring_booking_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "recurring_booking_groups" ADD CONSTRAINT "recurring_booking_groups_court_id_fkey" FOREIGN KEY ("court_id") REFERENCES "courts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "recurring_booking_groups" ADD CONSTRAINT "recurring_booking_groups_complex_id_fkey" FOREIGN KEY ("complex_id") REFERENCES "complexes"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -21,6 +21,7 @@ export const planSeeds = [
publicBookingPage: true,
advancedReports: false,
whatsappReminders: false,
fixedSlots: false,
},
pricing: {
overrides: {
@@ -51,6 +52,7 @@ export const planSeeds = [
publicBookingPage: true,
advancedReports: true,
whatsappReminders: true,
fixedSlots: true,
},
pricing: {
overrides: {
@@ -81,6 +83,7 @@ export const planSeeds = [
publicBookingPage: true,
advancedReports: true,
whatsappReminders: true,
fixedSlots: true,
},
pricing: {
overrides: {

View File

@@ -87,6 +87,11 @@ export type CourtBooking = Prisma.CourtBookingModel
*
*/
export type CourtBookingLog = Prisma.CourtBookingLogModel
/**
* Model RecurringBookingGroup
*
*/
export type RecurringBookingGroup = Prisma.RecurringBookingGroupModel
/**
* Model PasswordResetRequest
*

View File

@@ -111,6 +111,11 @@ export type CourtBooking = Prisma.CourtBookingModel
*
*/
export type CourtBookingLog = Prisma.CourtBookingLogModel
/**
* Model RecurringBookingGroup
*
*/
export type RecurringBookingGroup = Prisma.RecurringBookingGroupModel
/**
* Model PasswordResetRequest
*

View File

@@ -314,6 +314,18 @@ export type EnumCourtBookingStatusFilter<$PrismaModel = never> = {
not?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel> | $Enums.CourtBookingStatus
}
export type UuidNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedUuidNullableFilter<$PrismaModel> | string | null
}
export type EnumCourtBookingStatusWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.CourtBookingStatus | Prisma.EnumCourtBookingStatusFieldRefInput<$PrismaModel>
in?: $Enums.CourtBookingStatus[] | Prisma.ListEnumCourtBookingStatusFieldRefInput<$PrismaModel>
@@ -324,6 +336,38 @@ export type EnumCourtBookingStatusWithAggregatesFilter<$PrismaModel = never> = {
_max?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel>
}
export type UuidNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
mode?: Prisma.QueryMode
not?: Prisma.NestedUuidNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type EnumRecurringBookingGroupStatusFilter<$PrismaModel = never> = {
equals?: $Enums.RecurringBookingGroupStatus | Prisma.EnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
in?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
notIn?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel> | $Enums.RecurringBookingGroupStatus
}
export type EnumRecurringBookingGroupStatusWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.RecurringBookingGroupStatus | Prisma.EnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
in?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
notIn?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumRecurringBookingGroupStatusWithAggregatesFilter<$PrismaModel> | $Enums.RecurringBookingGroupStatus
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel>
_max?: Prisma.NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel>
}
export type JsonFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
@@ -686,6 +730,17 @@ export type NestedEnumCourtBookingStatusFilter<$PrismaModel = never> = {
not?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel> | $Enums.CourtBookingStatus
}
export type NestedUuidNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedUuidNullableFilter<$PrismaModel> | string | null
}
export type NestedEnumCourtBookingStatusWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.CourtBookingStatus | Prisma.EnumCourtBookingStatusFieldRefInput<$PrismaModel>
in?: $Enums.CourtBookingStatus[] | Prisma.ListEnumCourtBookingStatusFieldRefInput<$PrismaModel>
@@ -696,6 +751,37 @@ export type NestedEnumCourtBookingStatusWithAggregatesFilter<$PrismaModel = neve
_max?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel>
}
export type NestedUuidNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
not?: Prisma.NestedUuidNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel = never> = {
equals?: $Enums.RecurringBookingGroupStatus | Prisma.EnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
in?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
notIn?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel> | $Enums.RecurringBookingGroupStatus
}
export type NestedEnumRecurringBookingGroupStatusWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.RecurringBookingGroupStatus | Prisma.EnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
in?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
notIn?: $Enums.RecurringBookingGroupStatus[] | Prisma.ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumRecurringBookingGroupStatusWithAggregatesFilter<$PrismaModel> | $Enums.RecurringBookingGroupStatus
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel>
_max?: Prisma.NestedEnumRecurringBookingGroupStatusFilter<$PrismaModel>
}
export type NestedJsonFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,

View File

@@ -38,3 +38,11 @@ export const CourtBookingStatus = {
} as const
export type CourtBookingStatus = (typeof CourtBookingStatus)[keyof typeof CourtBookingStatus]
export const RecurringBookingGroupStatus = {
ACTIVE: 'ACTIVE',
CANCELLED: 'CANCELLED'
} as const
export type RecurringBookingGroupStatus = (typeof RecurringBookingGroupStatus)[keyof typeof RecurringBookingGroupStatus]

File diff suppressed because one or more lines are too long

View File

@@ -398,6 +398,7 @@ export const ModelName = {
CourtPriceRule: 'CourtPriceRule',
CourtBooking: 'CourtBooking',
CourtBookingLog: 'CourtBookingLog',
RecurringBookingGroup: 'RecurringBookingGroup',
PasswordResetRequest: 'PasswordResetRequest',
Plan: 'Plan'
} as const
@@ -415,7 +416,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
omit: GlobalOmitOptions
}
meta: {
modelProps: "user" | "session" | "account" | "verification" | "complex" | "complexUser" | "complexInvitation" | "sport" | "court" | "courtMaintenance" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "courtBookingLog" | "passwordResetRequest" | "plan"
modelProps: "user" | "session" | "account" | "verification" | "complex" | "complexUser" | "complexInvitation" | "sport" | "court" | "courtMaintenance" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "courtBookingLog" | "recurringBookingGroup" | "passwordResetRequest" | "plan"
txIsolationLevel: TransactionIsolationLevel
}
model: {
@@ -1455,6 +1456,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
}
}
}
RecurringBookingGroup: {
payload: Prisma.$RecurringBookingGroupPayload<ExtArgs>
fields: Prisma.RecurringBookingGroupFieldRefs
operations: {
findUnique: {
args: Prisma.RecurringBookingGroupFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload> | null
}
findUniqueOrThrow: {
args: Prisma.RecurringBookingGroupFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>
}
findFirst: {
args: Prisma.RecurringBookingGroupFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload> | null
}
findFirstOrThrow: {
args: Prisma.RecurringBookingGroupFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>
}
findMany: {
args: Prisma.RecurringBookingGroupFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>[]
}
create: {
args: Prisma.RecurringBookingGroupCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>
}
createMany: {
args: Prisma.RecurringBookingGroupCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.RecurringBookingGroupCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>[]
}
delete: {
args: Prisma.RecurringBookingGroupDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>
}
update: {
args: Prisma.RecurringBookingGroupUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>
}
deleteMany: {
args: Prisma.RecurringBookingGroupDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.RecurringBookingGroupUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.RecurringBookingGroupUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>[]
}
upsert: {
args: Prisma.RecurringBookingGroupUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$RecurringBookingGroupPayload>
}
aggregate: {
args: Prisma.RecurringBookingGroupAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateRecurringBookingGroup>
}
groupBy: {
args: Prisma.RecurringBookingGroupGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.RecurringBookingGroupGroupByOutputType>[]
}
count: {
args: Prisma.RecurringBookingGroupCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.RecurringBookingGroupCountAggregateOutputType> | number
}
}
}
PasswordResetRequest: {
payload: Prisma.$PasswordResetRequestPayload<ExtArgs>
fields: Prisma.PasswordResetRequestFieldRefs
@@ -1832,6 +1907,7 @@ export const CourtBookingScalarFieldEnum = {
customerPhone: 'customerPhone',
customerEmail: 'customerEmail',
status: 'status',
recurringGroupId: 'recurringGroupId',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
@@ -1857,6 +1933,26 @@ export const CourtBookingLogScalarFieldEnum = {
export type CourtBookingLogScalarFieldEnum = (typeof CourtBookingLogScalarFieldEnum)[keyof typeof CourtBookingLogScalarFieldEnum]
export const RecurringBookingGroupScalarFieldEnum = {
id: 'id',
complexId: 'complexId',
courtId: 'courtId',
startTime: 'startTime',
endTime: 'endTime',
dayOfWeek: 'dayOfWeek',
startDate: 'startDate',
endDate: 'endDate',
status: 'status',
customerName: 'customerName',
customerPhone: 'customerPhone',
customerEmail: 'customerEmail',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type RecurringBookingGroupScalarFieldEnum = (typeof RecurringBookingGroupScalarFieldEnum)[keyof typeof RecurringBookingGroupScalarFieldEnum]
export const PasswordResetRequestScalarFieldEnum = {
id: 'id',
email: 'email',
@@ -2049,6 +2145,20 @@ export type ListEnumCourtBookingStatusFieldRefInput<$PrismaModel> = FieldRefInpu
/**
* Reference to a field of type 'RecurringBookingGroupStatus'
*/
export type EnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'RecurringBookingGroupStatus'>
/**
* Reference to a field of type 'RecurringBookingGroupStatus[]'
*/
export type ListEnumRecurringBookingGroupStatusFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'RecurringBookingGroupStatus[]'>
/**
* Reference to a field of type 'Json'
*/
@@ -2171,6 +2281,7 @@ export type GlobalOmitConfig = {
courtPriceRule?: Prisma.CourtPriceRuleOmit
courtBooking?: Prisma.CourtBookingOmit
courtBookingLog?: Prisma.CourtBookingLogOmit
recurringBookingGroup?: Prisma.RecurringBookingGroupOmit
passwordResetRequest?: Prisma.PasswordResetRequestOmit
plan?: Prisma.PlanOmit
}

View File

@@ -65,6 +65,7 @@ export const ModelName = {
CourtPriceRule: 'CourtPriceRule',
CourtBooking: 'CourtBooking',
CourtBookingLog: 'CourtBookingLog',
RecurringBookingGroup: 'RecurringBookingGroup',
PasswordResetRequest: 'PasswordResetRequest',
Plan: 'Plan'
} as const
@@ -275,6 +276,7 @@ export const CourtBookingScalarFieldEnum = {
customerPhone: 'customerPhone',
customerEmail: 'customerEmail',
status: 'status',
recurringGroupId: 'recurringGroupId',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
@@ -300,6 +302,26 @@ export const CourtBookingLogScalarFieldEnum = {
export type CourtBookingLogScalarFieldEnum = (typeof CourtBookingLogScalarFieldEnum)[keyof typeof CourtBookingLogScalarFieldEnum]
export const RecurringBookingGroupScalarFieldEnum = {
id: 'id',
complexId: 'complexId',
courtId: 'courtId',
startTime: 'startTime',
endTime: 'endTime',
dayOfWeek: 'dayOfWeek',
startDate: 'startDate',
endDate: 'endDate',
status: 'status',
customerName: 'customerName',
customerPhone: 'customerPhone',
customerEmail: 'customerEmail',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type RecurringBookingGroupScalarFieldEnum = (typeof RecurringBookingGroupScalarFieldEnum)[keyof typeof RecurringBookingGroupScalarFieldEnum]
export const PasswordResetRequestScalarFieldEnum = {
id: 'id',
email: 'email',

View File

@@ -22,6 +22,7 @@ export type * from './models/CourtAvailability'
export type * from './models/CourtPriceRule'
export type * from './models/CourtBooking'
export type * from './models/CourtBookingLog'
export type * from './models/RecurringBookingGroup'
export type * from './models/PasswordResetRequest'
export type * from './models/Plan'
export type * from './commonInputTypes'

View File

@@ -234,6 +234,7 @@ export type ComplexWhereInput = {
users?: Prisma.ComplexUserListRelationFilter
invitations?: Prisma.ComplexInvitationListRelationFilter
courts?: Prisma.CourtListRelationFilter
recurringGroups?: Prisma.RecurringBookingGroupListRelationFilter
}
export type ComplexOrderByWithRelationInput = {
@@ -252,6 +253,7 @@ export type ComplexOrderByWithRelationInput = {
users?: Prisma.ComplexUserOrderByRelationAggregateInput
invitations?: Prisma.ComplexInvitationOrderByRelationAggregateInput
courts?: Prisma.CourtOrderByRelationAggregateInput
recurringGroups?: Prisma.RecurringBookingGroupOrderByRelationAggregateInput
}
export type ComplexWhereUniqueInput = Prisma.AtLeast<{
@@ -273,6 +275,7 @@ export type ComplexWhereUniqueInput = Prisma.AtLeast<{
users?: Prisma.ComplexUserListRelationFilter
invitations?: Prisma.ComplexInvitationListRelationFilter
courts?: Prisma.CourtListRelationFilter
recurringGroups?: Prisma.RecurringBookingGroupListRelationFilter
}, "id" | "complexSlug">
export type ComplexOrderByWithAggregationInput = {
@@ -324,6 +327,7 @@ export type ComplexCreateInput = {
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutComplexInput
}
export type ComplexUncheckedCreateInput = {
@@ -341,6 +345,7 @@ export type ComplexUncheckedCreateInput = {
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutComplexInput
}
export type ComplexUpdateInput = {
@@ -358,6 +363,7 @@ export type ComplexUpdateInput = {
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateInput = {
@@ -375,6 +381,7 @@ export type ComplexUncheckedUpdateInput = {
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutComplexNestedInput
}
export type ComplexCreateManyInput = {
@@ -517,6 +524,20 @@ export type ComplexUpdateOneRequiredWithoutCourtsNestedInput = {
update?: Prisma.XOR<Prisma.XOR<Prisma.ComplexUpdateToOneWithWhereWithoutCourtsInput, Prisma.ComplexUpdateWithoutCourtsInput>, Prisma.ComplexUncheckedUpdateWithoutCourtsInput>
}
export type ComplexCreateNestedOneWithoutRecurringGroupsInput = {
create?: Prisma.XOR<Prisma.ComplexCreateWithoutRecurringGroupsInput, Prisma.ComplexUncheckedCreateWithoutRecurringGroupsInput>
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutRecurringGroupsInput
connect?: Prisma.ComplexWhereUniqueInput
}
export type ComplexUpdateOneRequiredWithoutRecurringGroupsNestedInput = {
create?: Prisma.XOR<Prisma.ComplexCreateWithoutRecurringGroupsInput, Prisma.ComplexUncheckedCreateWithoutRecurringGroupsInput>
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutRecurringGroupsInput
upsert?: Prisma.ComplexUpsertWithoutRecurringGroupsInput
connect?: Prisma.ComplexWhereUniqueInput
update?: Prisma.XOR<Prisma.XOR<Prisma.ComplexUpdateToOneWithWhereWithoutRecurringGroupsInput, Prisma.ComplexUpdateWithoutRecurringGroupsInput>, Prisma.ComplexUncheckedUpdateWithoutRecurringGroupsInput>
}
export type ComplexCreateNestedManyWithoutPlanInput = {
create?: Prisma.XOR<Prisma.ComplexCreateWithoutPlanInput, Prisma.ComplexUncheckedCreateWithoutPlanInput> | Prisma.ComplexCreateWithoutPlanInput[] | Prisma.ComplexUncheckedCreateWithoutPlanInput[]
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutPlanInput | Prisma.ComplexCreateOrConnectWithoutPlanInput[]
@@ -573,6 +594,7 @@ export type ComplexCreateWithoutUsersInput = {
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutComplexInput
}
export type ComplexUncheckedCreateWithoutUsersInput = {
@@ -589,6 +611,7 @@ export type ComplexUncheckedCreateWithoutUsersInput = {
updatedAt?: Date | string
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutComplexInput
}
export type ComplexCreateOrConnectWithoutUsersInput = {
@@ -621,6 +644,7 @@ export type ComplexUpdateWithoutUsersInput = {
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateWithoutUsersInput = {
@@ -637,6 +661,7 @@ export type ComplexUncheckedUpdateWithoutUsersInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutComplexNestedInput
}
export type ComplexCreateWithoutInvitationsInput = {
@@ -653,6 +678,7 @@ export type ComplexCreateWithoutInvitationsInput = {
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutComplexInput
}
export type ComplexUncheckedCreateWithoutInvitationsInput = {
@@ -669,6 +695,7 @@ export type ComplexUncheckedCreateWithoutInvitationsInput = {
updatedAt?: Date | string
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutComplexInput
}
export type ComplexCreateOrConnectWithoutInvitationsInput = {
@@ -701,6 +728,7 @@ export type ComplexUpdateWithoutInvitationsInput = {
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateWithoutInvitationsInput = {
@@ -717,6 +745,7 @@ export type ComplexUncheckedUpdateWithoutInvitationsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutComplexNestedInput
}
export type ComplexCreateWithoutCourtsInput = {
@@ -733,6 +762,7 @@ export type ComplexCreateWithoutCourtsInput = {
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutComplexInput
}
export type ComplexUncheckedCreateWithoutCourtsInput = {
@@ -749,6 +779,7 @@ export type ComplexUncheckedCreateWithoutCourtsInput = {
updatedAt?: Date | string
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutComplexInput
}
export type ComplexCreateOrConnectWithoutCourtsInput = {
@@ -781,6 +812,7 @@ export type ComplexUpdateWithoutCourtsInput = {
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateWithoutCourtsInput = {
@@ -797,6 +829,91 @@ export type ComplexUncheckedUpdateWithoutCourtsInput = {
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutComplexNestedInput
}
export type ComplexCreateWithoutRecurringGroupsInput = {
id: string
complexName: string
physicalAddress?: string | null
city?: string | null
state?: string | null
country?: string | null
complexSlug: string
adminEmail: string
createdAt?: Date | string
updatedAt?: Date | string
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
}
export type ComplexUncheckedCreateWithoutRecurringGroupsInput = {
id: string
complexName: string
physicalAddress?: string | null
city?: string | null
state?: string | null
country?: string | null
complexSlug: string
adminEmail: string
planCode?: string | null
createdAt?: Date | string
updatedAt?: Date | string
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
}
export type ComplexCreateOrConnectWithoutRecurringGroupsInput = {
where: Prisma.ComplexWhereUniqueInput
create: Prisma.XOR<Prisma.ComplexCreateWithoutRecurringGroupsInput, Prisma.ComplexUncheckedCreateWithoutRecurringGroupsInput>
}
export type ComplexUpsertWithoutRecurringGroupsInput = {
update: Prisma.XOR<Prisma.ComplexUpdateWithoutRecurringGroupsInput, Prisma.ComplexUncheckedUpdateWithoutRecurringGroupsInput>
create: Prisma.XOR<Prisma.ComplexCreateWithoutRecurringGroupsInput, Prisma.ComplexUncheckedCreateWithoutRecurringGroupsInput>
where?: Prisma.ComplexWhereInput
}
export type ComplexUpdateToOneWithWhereWithoutRecurringGroupsInput = {
where?: Prisma.ComplexWhereInput
data: Prisma.XOR<Prisma.ComplexUpdateWithoutRecurringGroupsInput, Prisma.ComplexUncheckedUpdateWithoutRecurringGroupsInput>
}
export type ComplexUpdateWithoutRecurringGroupsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
complexName?: Prisma.StringFieldUpdateOperationsInput | string
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateWithoutRecurringGroupsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
complexName?: Prisma.StringFieldUpdateOperationsInput | string
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
}
export type ComplexCreateWithoutPlanInput = {
@@ -813,6 +930,7 @@ export type ComplexCreateWithoutPlanInput = {
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutComplexInput
}
export type ComplexUncheckedCreateWithoutPlanInput = {
@@ -829,6 +947,7 @@ export type ComplexUncheckedCreateWithoutPlanInput = {
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutComplexInput
}
export type ComplexCreateOrConnectWithoutPlanInput = {
@@ -901,6 +1020,7 @@ export type ComplexUpdateWithoutPlanInput = {
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateWithoutPlanInput = {
@@ -917,6 +1037,7 @@ export type ComplexUncheckedUpdateWithoutPlanInput = {
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutComplexNestedInput
}
export type ComplexUncheckedUpdateManyWithoutPlanInput = {
@@ -941,12 +1062,14 @@ export type ComplexCountOutputType = {
users: number
invitations: number
courts: number
recurringGroups: number
}
export type ComplexCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
users?: boolean | ComplexCountOutputTypeCountUsersArgs
invitations?: boolean | ComplexCountOutputTypeCountInvitationsArgs
courts?: boolean | ComplexCountOutputTypeCountCourtsArgs
recurringGroups?: boolean | ComplexCountOutputTypeCountRecurringGroupsArgs
}
/**
@@ -980,6 +1103,13 @@ export type ComplexCountOutputTypeCountCourtsArgs<ExtArgs extends runtime.Types.
where?: Prisma.CourtWhereInput
}
/**
* ComplexCountOutputType without action
*/
export type ComplexCountOutputTypeCountRecurringGroupsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
where?: Prisma.RecurringBookingGroupWhereInput
}
export type ComplexSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
@@ -997,6 +1127,7 @@ export type ComplexSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs
users?: boolean | Prisma.Complex$usersArgs<ExtArgs>
invitations?: boolean | Prisma.Complex$invitationsArgs<ExtArgs>
courts?: boolean | Prisma.Complex$courtsArgs<ExtArgs>
recurringGroups?: boolean | Prisma.Complex$recurringGroupsArgs<ExtArgs>
_count?: boolean | Prisma.ComplexCountOutputTypeDefaultArgs<ExtArgs>
}, ExtArgs["result"]["complex"]>
@@ -1050,6 +1181,7 @@ export type ComplexInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs
users?: boolean | Prisma.Complex$usersArgs<ExtArgs>
invitations?: boolean | Prisma.Complex$invitationsArgs<ExtArgs>
courts?: boolean | Prisma.Complex$courtsArgs<ExtArgs>
recurringGroups?: boolean | Prisma.Complex$recurringGroupsArgs<ExtArgs>
_count?: boolean | Prisma.ComplexCountOutputTypeDefaultArgs<ExtArgs>
}
export type ComplexIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
@@ -1066,6 +1198,7 @@ export type $ComplexPayload<ExtArgs extends runtime.Types.Extensions.InternalArg
users: Prisma.$ComplexUserPayload<ExtArgs>[]
invitations: Prisma.$ComplexInvitationPayload<ExtArgs>[]
courts: Prisma.$CourtPayload<ExtArgs>[]
recurringGroups: Prisma.$RecurringBookingGroupPayload<ExtArgs>[]
}
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
@@ -1477,6 +1610,7 @@ export interface Prisma__ComplexClient<T, Null = never, ExtArgs extends runtime.
users<T extends Prisma.Complex$usersArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$usersArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
invitations<T extends Prisma.Complex$invitationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$invitationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexInvitationPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
courts<T extends Prisma.Complex$courtsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$courtsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CourtPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
recurringGroups<T extends Prisma.Complex$recurringGroupsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$recurringGroupsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$RecurringBookingGroupPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
@@ -2008,6 +2142,30 @@ export type Complex$courtsArgs<ExtArgs extends runtime.Types.Extensions.Internal
distinct?: Prisma.CourtScalarFieldEnum | Prisma.CourtScalarFieldEnum[]
}
/**
* Complex.recurringGroups
*/
export type Complex$recurringGroupsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
/**
* Select specific fields to fetch from the RecurringBookingGroup
*/
select?: Prisma.RecurringBookingGroupSelect<ExtArgs> | null
/**
* Omit specific fields from the RecurringBookingGroup
*/
omit?: Prisma.RecurringBookingGroupOmit<ExtArgs> | null
/**
* Choose, which related nodes to fetch as well
*/
include?: Prisma.RecurringBookingGroupInclude<ExtArgs> | null
where?: Prisma.RecurringBookingGroupWhereInput
orderBy?: Prisma.RecurringBookingGroupOrderByWithRelationInput | Prisma.RecurringBookingGroupOrderByWithRelationInput[]
cursor?: Prisma.RecurringBookingGroupWhereUniqueInput
take?: number
skip?: number
distinct?: Prisma.RecurringBookingGroupScalarFieldEnum | Prisma.RecurringBookingGroupScalarFieldEnum[]
}
/**
* Complex without action
*/

View File

@@ -266,6 +266,7 @@ export type CourtWhereInput = {
priceRules?: Prisma.CourtPriceRuleListRelationFilter
bookings?: Prisma.CourtBookingListRelationFilter
maintenances?: Prisma.CourtMaintenanceListRelationFilter
recurringGroups?: Prisma.RecurringBookingGroupListRelationFilter
}
export type CourtOrderByWithRelationInput = {
@@ -285,6 +286,7 @@ export type CourtOrderByWithRelationInput = {
priceRules?: Prisma.CourtPriceRuleOrderByRelationAggregateInput
bookings?: Prisma.CourtBookingOrderByRelationAggregateInput
maintenances?: Prisma.CourtMaintenanceOrderByRelationAggregateInput
recurringGroups?: Prisma.RecurringBookingGroupOrderByRelationAggregateInput
}
export type CourtWhereUniqueInput = Prisma.AtLeast<{
@@ -307,6 +309,7 @@ export type CourtWhereUniqueInput = Prisma.AtLeast<{
priceRules?: Prisma.CourtPriceRuleListRelationFilter
bookings?: Prisma.CourtBookingListRelationFilter
maintenances?: Prisma.CourtMaintenanceListRelationFilter
recurringGroups?: Prisma.RecurringBookingGroupListRelationFilter
}, "id">
export type CourtOrderByWithAggregationInput = {
@@ -358,6 +361,7 @@ export type CourtCreateInput = {
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateInput = {
@@ -375,6 +379,7 @@ export type CourtUncheckedCreateInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtUpdateInput = {
@@ -392,6 +397,7 @@ export type CourtUpdateInput = {
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateInput = {
@@ -409,6 +415,7 @@ export type CourtUncheckedUpdateInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtCreateManyInput = {
@@ -668,6 +675,20 @@ export type CourtUpdateOneRequiredWithoutBookingsNestedInput = {
update?: Prisma.XOR<Prisma.XOR<Prisma.CourtUpdateToOneWithWhereWithoutBookingsInput, Prisma.CourtUpdateWithoutBookingsInput>, Prisma.CourtUncheckedUpdateWithoutBookingsInput>
}
export type CourtCreateNestedOneWithoutRecurringGroupsInput = {
create?: Prisma.XOR<Prisma.CourtCreateWithoutRecurringGroupsInput, Prisma.CourtUncheckedCreateWithoutRecurringGroupsInput>
connectOrCreate?: Prisma.CourtCreateOrConnectWithoutRecurringGroupsInput
connect?: Prisma.CourtWhereUniqueInput
}
export type CourtUpdateOneRequiredWithoutRecurringGroupsNestedInput = {
create?: Prisma.XOR<Prisma.CourtCreateWithoutRecurringGroupsInput, Prisma.CourtUncheckedCreateWithoutRecurringGroupsInput>
connectOrCreate?: Prisma.CourtCreateOrConnectWithoutRecurringGroupsInput
upsert?: Prisma.CourtUpsertWithoutRecurringGroupsInput
connect?: Prisma.CourtWhereUniqueInput
update?: Prisma.XOR<Prisma.XOR<Prisma.CourtUpdateToOneWithWhereWithoutRecurringGroupsInput, Prisma.CourtUpdateWithoutRecurringGroupsInput>, Prisma.CourtUncheckedUpdateWithoutRecurringGroupsInput>
}
export type CourtCreateWithoutComplexInput = {
id: string
name: string
@@ -682,6 +703,7 @@ export type CourtCreateWithoutComplexInput = {
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutComplexInput = {
@@ -698,6 +720,7 @@ export type CourtUncheckedCreateWithoutComplexInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutComplexInput = {
@@ -756,6 +779,7 @@ export type CourtCreateWithoutSportInput = {
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutSportInput = {
@@ -772,6 +796,7 @@ export type CourtUncheckedCreateWithoutSportInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutSportInput = {
@@ -814,6 +839,7 @@ export type CourtCreateWithoutMaintenancesInput = {
availabilities?: Prisma.CourtAvailabilityCreateNestedManyWithoutCourtInput
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutMaintenancesInput = {
@@ -830,6 +856,7 @@ export type CourtUncheckedCreateWithoutMaintenancesInput = {
availabilities?: Prisma.CourtAvailabilityUncheckedCreateNestedManyWithoutCourtInput
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutMaintenancesInput = {
@@ -862,6 +889,7 @@ export type CourtUpdateWithoutMaintenancesInput = {
availabilities?: Prisma.CourtAvailabilityUpdateManyWithoutCourtNestedInput
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutMaintenancesInput = {
@@ -878,6 +906,7 @@ export type CourtUncheckedUpdateWithoutMaintenancesInput = {
availabilities?: Prisma.CourtAvailabilityUncheckedUpdateManyWithoutCourtNestedInput
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtCreateWithoutAvailabilitiesInput = {
@@ -894,6 +923,7 @@ export type CourtCreateWithoutAvailabilitiesInput = {
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutAvailabilitiesInput = {
@@ -910,6 +940,7 @@ export type CourtUncheckedCreateWithoutAvailabilitiesInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutAvailabilitiesInput = {
@@ -942,6 +973,7 @@ export type CourtUpdateWithoutAvailabilitiesInput = {
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutAvailabilitiesInput = {
@@ -958,6 +990,7 @@ export type CourtUncheckedUpdateWithoutAvailabilitiesInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtCreateWithoutPriceRulesInput = {
@@ -974,6 +1007,7 @@ export type CourtCreateWithoutPriceRulesInput = {
availabilities?: Prisma.CourtAvailabilityCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutPriceRulesInput = {
@@ -990,6 +1024,7 @@ export type CourtUncheckedCreateWithoutPriceRulesInput = {
availabilities?: Prisma.CourtAvailabilityUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutPriceRulesInput = {
@@ -1022,6 +1057,7 @@ export type CourtUpdateWithoutPriceRulesInput = {
availabilities?: Prisma.CourtAvailabilityUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutPriceRulesInput = {
@@ -1038,6 +1074,7 @@ export type CourtUncheckedUpdateWithoutPriceRulesInput = {
availabilities?: Prisma.CourtAvailabilityUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtCreateWithoutBookingsInput = {
@@ -1054,6 +1091,7 @@ export type CourtCreateWithoutBookingsInput = {
availabilities?: Prisma.CourtAvailabilityCreateNestedManyWithoutCourtInput
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutBookingsInput = {
@@ -1070,6 +1108,7 @@ export type CourtUncheckedCreateWithoutBookingsInput = {
availabilities?: Prisma.CourtAvailabilityUncheckedCreateNestedManyWithoutCourtInput
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutBookingsInput = {
@@ -1102,6 +1141,7 @@ export type CourtUpdateWithoutBookingsInput = {
availabilities?: Prisma.CourtAvailabilityUpdateManyWithoutCourtNestedInput
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutBookingsInput = {
@@ -1118,6 +1158,91 @@ export type CourtUncheckedUpdateWithoutBookingsInput = {
availabilities?: Prisma.CourtAvailabilityUncheckedUpdateManyWithoutCourtNestedInput
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtCreateWithoutRecurringGroupsInput = {
id: string
name: string
slotDurationMinutes: number
basePrice: runtime.Decimal | runtime.DecimalJsLike | number | string
isUnderMaintenance?: boolean
maintenanceReason?: string | null
createdAt?: Date | string
updatedAt?: Date | string
complex: Prisma.ComplexCreateNestedOneWithoutCourtsInput
sport: Prisma.SportCreateNestedOneWithoutCourtsInput
availabilities?: Prisma.CourtAvailabilityCreateNestedManyWithoutCourtInput
priceRules?: Prisma.CourtPriceRuleCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceCreateNestedManyWithoutCourtInput
}
export type CourtUncheckedCreateWithoutRecurringGroupsInput = {
id: string
complexId: string
sportId: string
name: string
slotDurationMinutes: number
basePrice: runtime.Decimal | runtime.DecimalJsLike | number | string
isUnderMaintenance?: boolean
maintenanceReason?: string | null
createdAt?: Date | string
updatedAt?: Date | string
availabilities?: Prisma.CourtAvailabilityUncheckedCreateNestedManyWithoutCourtInput
priceRules?: Prisma.CourtPriceRuleUncheckedCreateNestedManyWithoutCourtInput
bookings?: Prisma.CourtBookingUncheckedCreateNestedManyWithoutCourtInput
maintenances?: Prisma.CourtMaintenanceUncheckedCreateNestedManyWithoutCourtInput
}
export type CourtCreateOrConnectWithoutRecurringGroupsInput = {
where: Prisma.CourtWhereUniqueInput
create: Prisma.XOR<Prisma.CourtCreateWithoutRecurringGroupsInput, Prisma.CourtUncheckedCreateWithoutRecurringGroupsInput>
}
export type CourtUpsertWithoutRecurringGroupsInput = {
update: Prisma.XOR<Prisma.CourtUpdateWithoutRecurringGroupsInput, Prisma.CourtUncheckedUpdateWithoutRecurringGroupsInput>
create: Prisma.XOR<Prisma.CourtCreateWithoutRecurringGroupsInput, Prisma.CourtUncheckedCreateWithoutRecurringGroupsInput>
where?: Prisma.CourtWhereInput
}
export type CourtUpdateToOneWithWhereWithoutRecurringGroupsInput = {
where?: Prisma.CourtWhereInput
data: Prisma.XOR<Prisma.CourtUpdateWithoutRecurringGroupsInput, Prisma.CourtUncheckedUpdateWithoutRecurringGroupsInput>
}
export type CourtUpdateWithoutRecurringGroupsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
slotDurationMinutes?: Prisma.IntFieldUpdateOperationsInput | number
basePrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string
isUnderMaintenance?: Prisma.BoolFieldUpdateOperationsInput | boolean
maintenanceReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
complex?: Prisma.ComplexUpdateOneRequiredWithoutCourtsNestedInput
sport?: Prisma.SportUpdateOneRequiredWithoutCourtsNestedInput
availabilities?: Prisma.CourtAvailabilityUpdateManyWithoutCourtNestedInput
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutRecurringGroupsInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
complexId?: Prisma.StringFieldUpdateOperationsInput | string
sportId?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
slotDurationMinutes?: Prisma.IntFieldUpdateOperationsInput | number
basePrice?: Prisma.DecimalFieldUpdateOperationsInput | runtime.Decimal | runtime.DecimalJsLike | number | string
isUnderMaintenance?: Prisma.BoolFieldUpdateOperationsInput | boolean
maintenanceReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
availabilities?: Prisma.CourtAvailabilityUncheckedUpdateManyWithoutCourtNestedInput
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtCreateManyComplexInput = {
@@ -1146,6 +1271,7 @@ export type CourtUpdateWithoutComplexInput = {
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutComplexInput = {
@@ -1162,6 +1288,7 @@ export type CourtUncheckedUpdateWithoutComplexInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateManyWithoutComplexInput = {
@@ -1202,6 +1329,7 @@ export type CourtUpdateWithoutSportInput = {
priceRules?: Prisma.CourtPriceRuleUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateWithoutSportInput = {
@@ -1218,6 +1346,7 @@ export type CourtUncheckedUpdateWithoutSportInput = {
priceRules?: Prisma.CourtPriceRuleUncheckedUpdateManyWithoutCourtNestedInput
bookings?: Prisma.CourtBookingUncheckedUpdateManyWithoutCourtNestedInput
maintenances?: Prisma.CourtMaintenanceUncheckedUpdateManyWithoutCourtNestedInput
recurringGroups?: Prisma.RecurringBookingGroupUncheckedUpdateManyWithoutCourtNestedInput
}
export type CourtUncheckedUpdateManyWithoutSportInput = {
@@ -1242,6 +1371,7 @@ export type CourtCountOutputType = {
priceRules: number
bookings: number
maintenances: number
recurringGroups: number
}
export type CourtCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
@@ -1249,6 +1379,7 @@ export type CourtCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.
priceRules?: boolean | CourtCountOutputTypeCountPriceRulesArgs
bookings?: boolean | CourtCountOutputTypeCountBookingsArgs
maintenances?: boolean | CourtCountOutputTypeCountMaintenancesArgs
recurringGroups?: boolean | CourtCountOutputTypeCountRecurringGroupsArgs
}
/**
@@ -1289,6 +1420,13 @@ export type CourtCountOutputTypeCountMaintenancesArgs<ExtArgs extends runtime.Ty
where?: Prisma.CourtMaintenanceWhereInput
}
/**
* CourtCountOutputType without action
*/
export type CourtCountOutputTypeCountRecurringGroupsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
where?: Prisma.RecurringBookingGroupWhereInput
}
export type CourtSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
@@ -1307,6 +1445,7 @@ export type CourtSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs =
priceRules?: boolean | Prisma.Court$priceRulesArgs<ExtArgs>
bookings?: boolean | Prisma.Court$bookingsArgs<ExtArgs>
maintenances?: boolean | Prisma.Court$maintenancesArgs<ExtArgs>
recurringGroups?: boolean | Prisma.Court$recurringGroupsArgs<ExtArgs>
_count?: boolean | Prisma.CourtCountOutputTypeDefaultArgs<ExtArgs>
}, ExtArgs["result"]["court"]>
@@ -1361,6 +1500,7 @@ export type CourtInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs =
priceRules?: boolean | Prisma.Court$priceRulesArgs<ExtArgs>
bookings?: boolean | Prisma.Court$bookingsArgs<ExtArgs>
maintenances?: boolean | Prisma.Court$maintenancesArgs<ExtArgs>
recurringGroups?: boolean | Prisma.Court$recurringGroupsArgs<ExtArgs>
_count?: boolean | Prisma.CourtCountOutputTypeDefaultArgs<ExtArgs>
}
export type CourtIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
@@ -1381,6 +1521,7 @@ export type $CourtPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs
priceRules: Prisma.$CourtPriceRulePayload<ExtArgs>[]
bookings: Prisma.$CourtBookingPayload<ExtArgs>[]
maintenances: Prisma.$CourtMaintenancePayload<ExtArgs>[]
recurringGroups: Prisma.$RecurringBookingGroupPayload<ExtArgs>[]
}
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
@@ -1793,6 +1934,7 @@ export interface Prisma__CourtClient<T, Null = never, ExtArgs extends runtime.Ty
priceRules<T extends Prisma.Court$priceRulesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Court$priceRulesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CourtPriceRulePayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
bookings<T extends Prisma.Court$bookingsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Court$bookingsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CourtBookingPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
maintenances<T extends Prisma.Court$maintenancesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Court$maintenancesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CourtMaintenancePayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
recurringGroups<T extends Prisma.Court$recurringGroupsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Court$recurringGroupsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$RecurringBookingGroupPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
@@ -2328,6 +2470,30 @@ export type Court$maintenancesArgs<ExtArgs extends runtime.Types.Extensions.Inte
distinct?: Prisma.CourtMaintenanceScalarFieldEnum | Prisma.CourtMaintenanceScalarFieldEnum[]
}
/**
* Court.recurringGroups
*/
export type Court$recurringGroupsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
/**
* Select specific fields to fetch from the RecurringBookingGroup
*/
select?: Prisma.RecurringBookingGroupSelect<ExtArgs> | null
/**
* Omit specific fields from the RecurringBookingGroup
*/
omit?: Prisma.RecurringBookingGroupOmit<ExtArgs> | null
/**
* Choose, which related nodes to fetch as well
*/
include?: Prisma.RecurringBookingGroupInclude<ExtArgs> | null
where?: Prisma.RecurringBookingGroupWhereInput
orderBy?: Prisma.RecurringBookingGroupOrderByWithRelationInput | Prisma.RecurringBookingGroupOrderByWithRelationInput[]
cursor?: Prisma.RecurringBookingGroupWhereUniqueInput
take?: number
skip?: number
distinct?: Prisma.RecurringBookingGroupScalarFieldEnum | Prisma.RecurringBookingGroupScalarFieldEnum[]
}
/**
* Court without action
*/

View File

@@ -35,6 +35,7 @@ export type CourtBookingMinAggregateOutputType = {
customerPhone: string | null
customerEmail: string | null
status: $Enums.CourtBookingStatus | null
recurringGroupId: string | null
createdAt: Date | null
updatedAt: Date | null
}
@@ -50,6 +51,7 @@ export type CourtBookingMaxAggregateOutputType = {
customerPhone: string | null
customerEmail: string | null
status: $Enums.CourtBookingStatus | null
recurringGroupId: string | null
createdAt: Date | null
updatedAt: Date | null
}
@@ -65,6 +67,7 @@ export type CourtBookingCountAggregateOutputType = {
customerPhone: number
customerEmail: number
status: number
recurringGroupId: number
createdAt: number
updatedAt: number
_all: number
@@ -82,6 +85,7 @@ export type CourtBookingMinAggregateInputType = {
customerPhone?: true
customerEmail?: true
status?: true
recurringGroupId?: true
createdAt?: true
updatedAt?: true
}
@@ -97,6 +101,7 @@ export type CourtBookingMaxAggregateInputType = {
customerPhone?: true
customerEmail?: true
status?: true
recurringGroupId?: true
createdAt?: true
updatedAt?: true
}
@@ -112,6 +117,7 @@ export type CourtBookingCountAggregateInputType = {
customerPhone?: true
customerEmail?: true
status?: true
recurringGroupId?: true
createdAt?: true
updatedAt?: true
_all?: true
@@ -200,6 +206,7 @@ export type CourtBookingGroupByOutputType = {
customerPhone: string
customerEmail: string
status: $Enums.CourtBookingStatus
recurringGroupId: string | null
createdAt: Date
updatedAt: Date
_count: CourtBookingCountAggregateOutputType | null
@@ -236,9 +243,11 @@ export type CourtBookingWhereInput = {
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
customerEmail?: Prisma.StringFilter<"CourtBooking"> | string
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.UuidNullableFilter<"CourtBooking"> | string | null
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
court?: Prisma.XOR<Prisma.CourtScalarRelationFilter, Prisma.CourtWhereInput>
recurringGroup?: Prisma.XOR<Prisma.RecurringBookingGroupNullableScalarRelationFilter, Prisma.RecurringBookingGroupWhereInput> | null
}
export type CourtBookingOrderByWithRelationInput = {
@@ -252,9 +261,11 @@ export type CourtBookingOrderByWithRelationInput = {
customerPhone?: Prisma.SortOrder
customerEmail?: Prisma.SortOrder
status?: Prisma.SortOrder
recurringGroupId?: Prisma.SortOrderInput | Prisma.SortOrder
createdAt?: Prisma.SortOrder
updatedAt?: Prisma.SortOrder
court?: Prisma.CourtOrderByWithRelationInput
recurringGroup?: Prisma.RecurringBookingGroupOrderByWithRelationInput
}
export type CourtBookingWhereUniqueInput = Prisma.AtLeast<{
@@ -272,9 +283,11 @@ export type CourtBookingWhereUniqueInput = Prisma.AtLeast<{
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
customerEmail?: Prisma.StringFilter<"CourtBooking"> | string
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.UuidNullableFilter<"CourtBooking"> | string | null
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
court?: Prisma.XOR<Prisma.CourtScalarRelationFilter, Prisma.CourtWhereInput>
recurringGroup?: Prisma.XOR<Prisma.RecurringBookingGroupNullableScalarRelationFilter, Prisma.RecurringBookingGroupWhereInput> | null
}, "id" | "bookingCode" | "courtId_bookingDate_startTime">
export type CourtBookingOrderByWithAggregationInput = {
@@ -288,6 +301,7 @@ export type CourtBookingOrderByWithAggregationInput = {
customerPhone?: Prisma.SortOrder
customerEmail?: Prisma.SortOrder
status?: Prisma.SortOrder
recurringGroupId?: Prisma.SortOrderInput | Prisma.SortOrder
createdAt?: Prisma.SortOrder
updatedAt?: Prisma.SortOrder
_count?: Prisma.CourtBookingCountOrderByAggregateInput
@@ -309,6 +323,7 @@ export type CourtBookingScalarWhereWithAggregatesInput = {
customerPhone?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
customerEmail?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
status?: Prisma.EnumCourtBookingStatusWithAggregatesFilter<"CourtBooking"> | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.UuidNullableWithAggregatesFilter<"CourtBooking"> | string | null
createdAt?: Prisma.DateTimeWithAggregatesFilter<"CourtBooking"> | Date | string
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CourtBooking"> | Date | string
}
@@ -326,6 +341,7 @@ export type CourtBookingCreateInput = {
createdAt?: Date | string
updatedAt?: Date | string
court: Prisma.CourtCreateNestedOneWithoutBookingsInput
recurringGroup?: Prisma.RecurringBookingGroupCreateNestedOneWithoutBookingsInput
}
export type CourtBookingUncheckedCreateInput = {
@@ -339,6 +355,7 @@ export type CourtBookingUncheckedCreateInput = {
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
recurringGroupId?: string | null
createdAt?: Date | string
updatedAt?: Date | string
}
@@ -356,6 +373,7 @@ export type CourtBookingUpdateInput = {
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
court?: Prisma.CourtUpdateOneRequiredWithoutBookingsNestedInput
recurringGroup?: Prisma.RecurringBookingGroupUpdateOneWithoutBookingsNestedInput
}
export type CourtBookingUncheckedUpdateInput = {
@@ -369,6 +387,7 @@ export type CourtBookingUncheckedUpdateInput = {
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
}
@@ -384,6 +403,7 @@ export type CourtBookingCreateManyInput = {
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
recurringGroupId?: string | null
createdAt?: Date | string
updatedAt?: Date | string
}
@@ -413,6 +433,7 @@ export type CourtBookingUncheckedUpdateManyInput = {
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
}
@@ -444,6 +465,7 @@ export type CourtBookingCountOrderByAggregateInput = {
customerPhone?: Prisma.SortOrder
customerEmail?: Prisma.SortOrder
status?: Prisma.SortOrder
recurringGroupId?: Prisma.SortOrder
createdAt?: Prisma.SortOrder
updatedAt?: Prisma.SortOrder
}
@@ -459,6 +481,7 @@ export type CourtBookingMaxOrderByAggregateInput = {
customerPhone?: Prisma.SortOrder
customerEmail?: Prisma.SortOrder
status?: Prisma.SortOrder
recurringGroupId?: Prisma.SortOrder
createdAt?: Prisma.SortOrder
updatedAt?: Prisma.SortOrder
}
@@ -474,6 +497,7 @@ export type CourtBookingMinOrderByAggregateInput = {
customerPhone?: Prisma.SortOrder
customerEmail?: Prisma.SortOrder
status?: Prisma.SortOrder
recurringGroupId?: Prisma.SortOrder
createdAt?: Prisma.SortOrder
updatedAt?: Prisma.SortOrder
}
@@ -524,6 +548,48 @@ export type EnumCourtBookingStatusFieldUpdateOperationsInput = {
set?: $Enums.CourtBookingStatus
}
export type CourtBookingCreateNestedManyWithoutRecurringGroupInput = {
create?: Prisma.XOR<Prisma.CourtBookingCreateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput> | Prisma.CourtBookingCreateWithoutRecurringGroupInput[] | Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput[]
connectOrCreate?: Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput | Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput[]
createMany?: Prisma.CourtBookingCreateManyRecurringGroupInputEnvelope
connect?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
}
export type CourtBookingUncheckedCreateNestedManyWithoutRecurringGroupInput = {
create?: Prisma.XOR<Prisma.CourtBookingCreateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput> | Prisma.CourtBookingCreateWithoutRecurringGroupInput[] | Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput[]
connectOrCreate?: Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput | Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput[]
createMany?: Prisma.CourtBookingCreateManyRecurringGroupInputEnvelope
connect?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
}
export type CourtBookingUpdateManyWithoutRecurringGroupNestedInput = {
create?: Prisma.XOR<Prisma.CourtBookingCreateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput> | Prisma.CourtBookingCreateWithoutRecurringGroupInput[] | Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput[]
connectOrCreate?: Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput | Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput[]
upsert?: Prisma.CourtBookingUpsertWithWhereUniqueWithoutRecurringGroupInput | Prisma.CourtBookingUpsertWithWhereUniqueWithoutRecurringGroupInput[]
createMany?: Prisma.CourtBookingCreateManyRecurringGroupInputEnvelope
set?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
disconnect?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
delete?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
connect?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
update?: Prisma.CourtBookingUpdateWithWhereUniqueWithoutRecurringGroupInput | Prisma.CourtBookingUpdateWithWhereUniqueWithoutRecurringGroupInput[]
updateMany?: Prisma.CourtBookingUpdateManyWithWhereWithoutRecurringGroupInput | Prisma.CourtBookingUpdateManyWithWhereWithoutRecurringGroupInput[]
deleteMany?: Prisma.CourtBookingScalarWhereInput | Prisma.CourtBookingScalarWhereInput[]
}
export type CourtBookingUncheckedUpdateManyWithoutRecurringGroupNestedInput = {
create?: Prisma.XOR<Prisma.CourtBookingCreateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput> | Prisma.CourtBookingCreateWithoutRecurringGroupInput[] | Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput[]
connectOrCreate?: Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput | Prisma.CourtBookingCreateOrConnectWithoutRecurringGroupInput[]
upsert?: Prisma.CourtBookingUpsertWithWhereUniqueWithoutRecurringGroupInput | Prisma.CourtBookingUpsertWithWhereUniqueWithoutRecurringGroupInput[]
createMany?: Prisma.CourtBookingCreateManyRecurringGroupInputEnvelope
set?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
disconnect?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
delete?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
connect?: Prisma.CourtBookingWhereUniqueInput | Prisma.CourtBookingWhereUniqueInput[]
update?: Prisma.CourtBookingUpdateWithWhereUniqueWithoutRecurringGroupInput | Prisma.CourtBookingUpdateWithWhereUniqueWithoutRecurringGroupInput[]
updateMany?: Prisma.CourtBookingUpdateManyWithWhereWithoutRecurringGroupInput | Prisma.CourtBookingUpdateManyWithWhereWithoutRecurringGroupInput[]
deleteMany?: Prisma.CourtBookingScalarWhereInput | Prisma.CourtBookingScalarWhereInput[]
}
export type CourtBookingCreateWithoutCourtInput = {
id: string
bookingCode: string
@@ -536,6 +602,7 @@ export type CourtBookingCreateWithoutCourtInput = {
status?: $Enums.CourtBookingStatus
createdAt?: Date | string
updatedAt?: Date | string
recurringGroup?: Prisma.RecurringBookingGroupCreateNestedOneWithoutBookingsInput
}
export type CourtBookingUncheckedCreateWithoutCourtInput = {
@@ -548,6 +615,7 @@ export type CourtBookingUncheckedCreateWithoutCourtInput = {
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
recurringGroupId?: string | null
createdAt?: Date | string
updatedAt?: Date | string
}
@@ -592,10 +660,67 @@ export type CourtBookingScalarWhereInput = {
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
customerEmail?: Prisma.StringFilter<"CourtBooking"> | string
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.UuidNullableFilter<"CourtBooking"> | string | null
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
}
export type CourtBookingCreateWithoutRecurringGroupInput = {
id: string
bookingCode: string
bookingDate: Date | string
startTime: string
endTime: string
customerName: string
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
createdAt?: Date | string
updatedAt?: Date | string
court: Prisma.CourtCreateNestedOneWithoutBookingsInput
}
export type CourtBookingUncheckedCreateWithoutRecurringGroupInput = {
id: string
bookingCode: string
courtId: string
bookingDate: Date | string
startTime: string
endTime: string
customerName: string
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
createdAt?: Date | string
updatedAt?: Date | string
}
export type CourtBookingCreateOrConnectWithoutRecurringGroupInput = {
where: Prisma.CourtBookingWhereUniqueInput
create: Prisma.XOR<Prisma.CourtBookingCreateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput>
}
export type CourtBookingCreateManyRecurringGroupInputEnvelope = {
data: Prisma.CourtBookingCreateManyRecurringGroupInput | Prisma.CourtBookingCreateManyRecurringGroupInput[]
skipDuplicates?: boolean
}
export type CourtBookingUpsertWithWhereUniqueWithoutRecurringGroupInput = {
where: Prisma.CourtBookingWhereUniqueInput
update: Prisma.XOR<Prisma.CourtBookingUpdateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedUpdateWithoutRecurringGroupInput>
create: Prisma.XOR<Prisma.CourtBookingCreateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedCreateWithoutRecurringGroupInput>
}
export type CourtBookingUpdateWithWhereUniqueWithoutRecurringGroupInput = {
where: Prisma.CourtBookingWhereUniqueInput
data: Prisma.XOR<Prisma.CourtBookingUpdateWithoutRecurringGroupInput, Prisma.CourtBookingUncheckedUpdateWithoutRecurringGroupInput>
}
export type CourtBookingUpdateManyWithWhereWithoutRecurringGroupInput = {
where: Prisma.CourtBookingScalarWhereInput
data: Prisma.XOR<Prisma.CourtBookingUpdateManyMutationInput, Prisma.CourtBookingUncheckedUpdateManyWithoutRecurringGroupInput>
}
export type CourtBookingCreateManyCourtInput = {
id: string
bookingCode: string
@@ -606,6 +731,7 @@ export type CourtBookingCreateManyCourtInput = {
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
recurringGroupId?: string | null
createdAt?: Date | string
updatedAt?: Date | string
}
@@ -622,6 +748,7 @@ export type CourtBookingUpdateWithoutCourtInput = {
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
recurringGroup?: Prisma.RecurringBookingGroupUpdateOneWithoutBookingsNestedInput
}
export type CourtBookingUncheckedUpdateWithoutCourtInput = {
@@ -634,6 +761,7 @@ export type CourtBookingUncheckedUpdateWithoutCourtInput = {
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
}
@@ -648,6 +776,67 @@ export type CourtBookingUncheckedUpdateManyWithoutCourtInput = {
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
recurringGroupId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
}
export type CourtBookingCreateManyRecurringGroupInput = {
id: string
bookingCode: string
courtId: string
bookingDate: Date | string
startTime: string
endTime: string
customerName: string
customerPhone: string
customerEmail: string
status?: $Enums.CourtBookingStatus
createdAt?: Date | string
updatedAt?: Date | string
}
export type CourtBookingUpdateWithoutRecurringGroupInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
bookingCode?: Prisma.StringFieldUpdateOperationsInput | string
bookingDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
startTime?: Prisma.StringFieldUpdateOperationsInput | string
endTime?: Prisma.StringFieldUpdateOperationsInput | string
customerName?: Prisma.StringFieldUpdateOperationsInput | string
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
court?: Prisma.CourtUpdateOneRequiredWithoutBookingsNestedInput
}
export type CourtBookingUncheckedUpdateWithoutRecurringGroupInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
bookingCode?: Prisma.StringFieldUpdateOperationsInput | string
courtId?: Prisma.StringFieldUpdateOperationsInput | string
bookingDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
startTime?: Prisma.StringFieldUpdateOperationsInput | string
endTime?: Prisma.StringFieldUpdateOperationsInput | string
customerName?: Prisma.StringFieldUpdateOperationsInput | string
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
}
export type CourtBookingUncheckedUpdateManyWithoutRecurringGroupInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
bookingCode?: Prisma.StringFieldUpdateOperationsInput | string
courtId?: Prisma.StringFieldUpdateOperationsInput | string
bookingDate?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
startTime?: Prisma.StringFieldUpdateOperationsInput | string
endTime?: Prisma.StringFieldUpdateOperationsInput | string
customerName?: Prisma.StringFieldUpdateOperationsInput | string
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
customerEmail?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
}
@@ -665,9 +854,11 @@ export type CourtBookingSelect<ExtArgs extends runtime.Types.Extensions.Internal
customerPhone?: boolean
customerEmail?: boolean
status?: boolean
recurringGroupId?: boolean
createdAt?: boolean
updatedAt?: boolean
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
recurringGroup?: boolean | Prisma.CourtBooking$recurringGroupArgs<ExtArgs>
}, ExtArgs["result"]["courtBooking"]>
export type CourtBookingSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
@@ -681,9 +872,11 @@ export type CourtBookingSelectCreateManyAndReturn<ExtArgs extends runtime.Types.
customerPhone?: boolean
customerEmail?: boolean
status?: boolean
recurringGroupId?: boolean
createdAt?: boolean
updatedAt?: boolean
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
recurringGroup?: boolean | Prisma.CourtBooking$recurringGroupArgs<ExtArgs>
}, ExtArgs["result"]["courtBooking"]>
export type CourtBookingSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
@@ -697,9 +890,11 @@ export type CourtBookingSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.
customerPhone?: boolean
customerEmail?: boolean
status?: boolean
recurringGroupId?: boolean
createdAt?: boolean
updatedAt?: boolean
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
recurringGroup?: boolean | Prisma.CourtBooking$recurringGroupArgs<ExtArgs>
}, ExtArgs["result"]["courtBooking"]>
export type CourtBookingSelectScalar = {
@@ -713,25 +908,30 @@ export type CourtBookingSelectScalar = {
customerPhone?: boolean
customerEmail?: boolean
status?: boolean
recurringGroupId?: boolean
createdAt?: boolean
updatedAt?: boolean
}
export type CourtBookingOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "bookingCode" | "courtId" | "bookingDate" | "startTime" | "endTime" | "customerName" | "customerPhone" | "customerEmail" | "status" | "createdAt" | "updatedAt", ExtArgs["result"]["courtBooking"]>
export type CourtBookingOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "bookingCode" | "courtId" | "bookingDate" | "startTime" | "endTime" | "customerName" | "customerPhone" | "customerEmail" | "status" | "recurringGroupId" | "createdAt" | "updatedAt", ExtArgs["result"]["courtBooking"]>
export type CourtBookingInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
recurringGroup?: boolean | Prisma.CourtBooking$recurringGroupArgs<ExtArgs>
}
export type CourtBookingIncludeCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
recurringGroup?: boolean | Prisma.CourtBooking$recurringGroupArgs<ExtArgs>
}
export type CourtBookingIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
recurringGroup?: boolean | Prisma.CourtBooking$recurringGroupArgs<ExtArgs>
}
export type $CourtBookingPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
name: "CourtBooking"
objects: {
court: Prisma.$CourtPayload<ExtArgs>
recurringGroup: Prisma.$RecurringBookingGroupPayload<ExtArgs> | null
}
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
@@ -744,6 +944,7 @@ export type $CourtBookingPayload<ExtArgs extends runtime.Types.Extensions.Intern
customerPhone: string
customerEmail: string
status: $Enums.CourtBookingStatus
recurringGroupId: string | null
createdAt: Date
updatedAt: Date
}, ExtArgs["result"]["courtBooking"]>
@@ -1141,6 +1342,7 @@ readonly fields: CourtBookingFieldRefs;
export interface Prisma__CourtBookingClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
readonly [Symbol.toStringTag]: "PrismaPromise"
court<T extends Prisma.CourtDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.CourtDefaultArgs<ExtArgs>>): Prisma.Prisma__CourtClient<runtime.Types.Result.GetResult<Prisma.$CourtPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>
recurringGroup<T extends Prisma.CourtBooking$recurringGroupArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.CourtBooking$recurringGroupArgs<ExtArgs>>): Prisma.Prisma__RecurringBookingGroupClient<runtime.Types.Result.GetResult<Prisma.$RecurringBookingGroupPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
@@ -1180,6 +1382,7 @@ export interface CourtBookingFieldRefs {
readonly customerPhone: Prisma.FieldRef<"CourtBooking", 'String'>
readonly customerEmail: Prisma.FieldRef<"CourtBooking", 'String'>
readonly status: Prisma.FieldRef<"CourtBooking", 'CourtBookingStatus'>
readonly recurringGroupId: Prisma.FieldRef<"CourtBooking", 'String'>
readonly createdAt: Prisma.FieldRef<"CourtBooking", 'DateTime'>
readonly updatedAt: Prisma.FieldRef<"CourtBooking", 'DateTime'>
}
@@ -1582,6 +1785,25 @@ export type CourtBookingDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.
limit?: number
}
/**
* CourtBooking.recurringGroup
*/
export type CourtBooking$recurringGroupArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
/**
* Select specific fields to fetch from the RecurringBookingGroup
*/
select?: Prisma.RecurringBookingGroupSelect<ExtArgs> | null
/**
* Omit specific fields from the RecurringBookingGroup
*/
omit?: Prisma.RecurringBookingGroupOmit<ExtArgs> | null
/**
* Choose, which related nodes to fetch as well
*/
include?: Prisma.RecurringBookingGroupInclude<ExtArgs> | null
where?: Prisma.RecurringBookingGroupWhereInput
}
/**
* CourtBooking without action
*/

View File

@@ -1,11 +1,14 @@
import { requireAuth } from '@/middlewares/require-auth.middleware';
import { cancelRecurringGroupHandler } from '@/modules/admin-booking/handlers/cancel-recurring-group.handler';
import { createAdminBookingHandler } from '@/modules/admin-booking/handlers/create-admin-booking.handler';
import { createAdminRecurringBookingHandler } from '@/modules/admin-booking/handlers/create-admin-recurring-booking.handler';
import { listAdminBookingsHandler } from '@/modules/admin-booking/handlers/list-admin-bookings.handler';
import { updateAdminBookingStatusHandler } from '@/modules/admin-booking/handlers/update-admin-booking-status.handler';
import type { AppEnv } from '@/types/hono';
import { zValidator } from '@hono/zod-validator';
import {
createAdminBookingSchema,
createRecurringBookingSchema,
listAdminBookingsQuerySchema,
updateAdminBookingStatusSchema,
} from '@repo/api-contract';
@@ -15,6 +18,7 @@ import { z } from 'zod';
export const adminBookingRoutes = new Hono<AppEnv>();
const complexIdParamsSchema = z.object({ complexId: z.uuid() });
const bookingIdParamsSchema = z.object({ id: z.uuid() });
const groupIdParamsSchema = z.object({ groupId: z.uuid() });
adminBookingRoutes.use('*', requireAuth);
@@ -32,6 +36,19 @@ adminBookingRoutes.post(
createAdminBookingHandler
);
adminBookingRoutes.post(
'/complex/:complexId/recurring',
zValidator('param', complexIdParamsSchema),
zValidator('json', createRecurringBookingSchema),
createAdminRecurringBookingHandler
);
adminBookingRoutes.post(
'/recurring/:groupId/cancel',
zValidator('param', groupIdParamsSchema),
cancelRecurringGroupHandler
);
adminBookingRoutes.patch(
'/:id/status',
zValidator('param', bookingIdParamsSchema),

View File

@@ -0,0 +1,20 @@
import { AdminBookingServiceError } from '@/modules/admin-booking/services/admin-booking.service';
import { cancelRecurringGroup } from '@/modules/admin-booking/services/admin-recurring-booking.service';
import type { AppContext } from '@/types/hono';
type GroupIdParams = { groupId: string };
export async function cancelRecurringGroupHandler(c: AppContext) {
const { groupId } = c.req.valid('param' as never) as GroupIdParams;
const user = c.get('user');
try {
const result = await cancelRecurringGroup(user.id, groupId);
return c.json(result);
} catch (error) {
if (error instanceof AdminBookingServiceError) {
return c.json({ message: error.message }, error.status);
}
throw error;
}
}

View File

@@ -0,0 +1,40 @@
import { AdminBookingServiceError } from '@/modules/admin-booking/services/admin-booking.service';
import { createAdminRecurringBooking } from '@/modules/admin-booking/services/admin-recurring-booking.service';
import { sendBookingConfirmation } from '@/services/booking-email.service';
import type { AppContext } from '@/types/hono';
import type { CreateRecurringBookingInput } from '@repo/api-contract';
type ComplexIdParams = { complexId: string };
export async function createAdminRecurringBookingHandler(c: AppContext) {
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
const payload = c.req.valid('json' as never) as CreateRecurringBookingInput;
const user = c.get('user');
try {
const group = await createAdminRecurringBooking(user.id, complexId, payload);
const firstBooking = group.bookings[0];
if (firstBooking?.customerEmail) {
void sendBookingConfirmation({
bookingCode: firstBooking.bookingCode,
complexName: firstBooking.complexName,
date: firstBooking.date,
startTime: firstBooking.startTime,
endTime: firstBooking.endTime,
courtName: firstBooking.courtName,
sportName: firstBooking.sport.name,
customerName: firstBooking.customerName,
customerEmail: firstBooking.customerEmail,
price: firstBooking.price,
});
}
return c.json(group, 201);
} catch (error) {
if (error instanceof AdminBookingServiceError) {
return c.json({ message: error.message }, error.status);
}
throw error;
}
}

View File

@@ -202,6 +202,7 @@ function mapBookingResponse(booking: {
customerPhone: string;
customerEmail: string;
status: 'CONFIRMED' | 'CANCELLED' | 'COMPLETED' | 'NOSHOW';
recurringGroupId: string | null;
createdAt: Date;
updatedAt: Date;
court: {
@@ -239,6 +240,7 @@ function mapBookingResponse(booking: {
customerEmail: booking.customerEmail,
price: booking.price ?? 0,
status: booking.status,
recurringGroupId: booking.recurringGroupId,
createdAt: booking.createdAt.toISOString(),
updatedAt: booking.updatedAt.toISOString(),
};
@@ -431,6 +433,7 @@ export async function createAdminBooking(
customerPhone: input.customerPhone.trim(),
customerEmail: input.customerEmail?.trim() ?? '',
status: 'CONFIRMED',
recurringGroupId: null,
},
include: {
court: {

View File

@@ -0,0 +1,576 @@
import { randomInt } from 'node:crypto';
import { DayOfWeek as DayOfWeekEnum } from '@/generated/prisma/enums';
import { db } from '@/lib/prisma';
import { isSlotInPast } from '@/lib/slot-validator';
import {
evaluatePlanUsage,
isFeatureEnabled,
parsePlanRules,
} from '@/modules/plan/services/plan-rules.service';
import type { CreateRecurringBookingInput, RecurringBookingGroup } from '@repo/api-contract';
import { v7 as uuidv7 } from 'uuid';
import { AdminBookingServiceError } from './admin-booking.service';
const BOOKING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
const BOOKING_CODE_LENGTH = 6;
const MAX_RECURRING_WEEKS = 52;
const DAY_OF_WEEK_BY_INDEX = [
DayOfWeekEnum.SUNDAY,
DayOfWeekEnum.MONDAY,
DayOfWeekEnum.TUESDAY,
DayOfWeekEnum.WEDNESDAY,
DayOfWeekEnum.THURSDAY,
DayOfWeekEnum.FRIDAY,
DayOfWeekEnum.SATURDAY,
] as const;
type DayOfWeek = (typeof DAY_OF_WEEK_BY_INDEX)[number];
function toMinutes(value: string): number {
const [hours, minutes] = value.split(':').map((part) => Number(part));
return hours * 60 + minutes;
}
function minutesToTime(minutes: number): string {
const safeMinutes = Math.max(0, minutes);
const hours = Math.floor(safeMinutes / 60);
const mins = safeMinutes % 60;
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
}
function parseIsoDate(date: string): Date {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
if (!match) {
throw new AdminBookingServiceError('La fecha debe tener formato YYYY-MM-DD.', 400);
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
const parsed = new Date(Date.UTC(year, month - 1, day));
if (
Number.isNaN(parsed.getTime()) ||
parsed.getUTCFullYear() !== year ||
parsed.getUTCMonth() + 1 !== month ||
parsed.getUTCDate() !== day
) {
throw new AdminBookingServiceError('La fecha enviada no es valida.', 400);
}
return parsed;
}
function formatIsoDate(date: Date): string {
return date.toISOString().slice(0, 10);
}
function getDayOfWeekValue(date: Date): DayOfWeek {
const dayOfWeek = DAY_OF_WEEK_BY_INDEX[date.getUTCDay()];
if (!dayOfWeek) {
throw new AdminBookingServiceError('No se pudo resolver el dia de la semana.', 400);
}
return dayOfWeek;
}
function generateBookingCode(): string {
let code = '';
for (let index = 0; index < BOOKING_CODE_LENGTH; index += 1) {
code += BOOKING_CODE_ALPHABET[randomInt(0, BOOKING_CODE_ALPHABET.length)];
}
return code;
}
function buildSlots(
availability: Array<{
startTime: string;
endTime: string;
}>,
slotDurationMinutes: number
) {
const slots: Array<{ startTime: string; endTime: string }> = [];
for (const range of availability) {
const start = toMinutes(range.startTime);
const end = toMinutes(range.endTime);
for (
let current = start;
current + slotDurationMinutes <= end;
current += slotDurationMinutes
) {
slots.push({
startTime: minutesToTime(current),
endTime: minutesToTime(current + slotDurationMinutes),
});
}
}
return slots;
}
async function ensureComplexAccess(complexId: string, userId: string) {
const complexUser = await db.complexUser.findUnique({
where: {
complexId_userId: {
complexId,
userId,
},
},
include: {
complex: {
select: {
id: true,
complexName: true,
plan: {
select: {
rules: true,
},
},
},
},
},
});
if (!complexUser) {
throw new AdminBookingServiceError('No tienes permisos para administrar este complejo.', 403);
}
return complexUser.complex;
}
function* generateRecurringDates(
startDate: Date,
endDate: Date | null,
dayOfWeek: number
): Generator<Date> {
const current = new Date(startDate);
current.setUTCDate(current.getUTCDate() + ((dayOfWeek - current.getUTCDay() + 7) % 7));
if (current < startDate) {
current.setUTCDate(current.getUTCDate() + 7);
}
const maxDate = endDate ?? new Date(startDate);
if (!endDate) {
maxDate.setUTCDate(maxDate.getUTCDate() + MAX_RECURRING_WEEKS * 7);
}
while (current <= maxDate) {
yield new Date(current);
current.setUTCDate(current.getUTCDate() + 7);
}
}
function mapBookingResponse(booking: {
id: string;
bookingCode: string;
bookingDate: Date;
startTime: string;
endTime: string;
customerName: string;
customerPhone: string;
customerEmail: string;
status: 'CONFIRMED' | 'CANCELLED' | 'COMPLETED' | 'NOSHOW';
recurringGroupId: string | null;
createdAt: Date;
updatedAt: Date;
court: {
id: string;
name: string;
complex: {
id: string;
complexName: string;
};
sport: {
id: string;
name: string;
slug: string;
};
};
price?: number;
}) {
return {
id: booking.id,
bookingCode: booking.bookingCode,
complexId: booking.court.complex.id,
complexName: booking.court.complex.complexName,
courtId: booking.court.id,
courtName: booking.court.name,
sport: {
id: booking.court.sport.id,
name: booking.court.sport.name,
slug: booking.court.sport.slug,
},
date: formatIsoDate(booking.bookingDate),
startTime: booking.startTime,
endTime: booking.endTime,
customerName: booking.customerName,
customerPhone: booking.customerPhone,
customerEmail: booking.customerEmail,
price: booking.price ?? 0,
status: booking.status,
recurringGroupId: booking.recurringGroupId,
createdAt: booking.createdAt.toISOString(),
updatedAt: booking.updatedAt.toISOString(),
};
}
export async function createAdminRecurringBooking(
userId: string,
complexId: string,
input: CreateRecurringBookingInput
): Promise<RecurringBookingGroup> {
const complex = await ensureComplexAccess(complexId, userId);
const adminUser = await db.user.findUnique({
where: { id: userId },
select: { emailVerified: true },
});
if (!adminUser?.emailVerified) {
throw new AdminBookingServiceError('Debés verificar tu email para poder crear reservas.', 403);
}
if (!complex.plan) {
throw new AdminBookingServiceError('El complejo no tiene un plan asignado.', 403);
}
const rules = parsePlanRules(complex.plan.rules);
if (!isFeatureEnabled(rules, 'fixedSlots')) {
throw new AdminBookingServiceError(
'Tu plan no permite la creación de turnos fijos. Comunicate con el administrador.',
403
);
}
const startDate = parseIsoDate(input.date);
const dayOfWeek = getDayOfWeekValue(startDate);
const endDate = input.recurringEndDate ? parseIsoDate(input.recurringEndDate) : null;
if (endDate && endDate <= startDate) {
throw new AdminBookingServiceError(
'La fecha de fin debe ser posterior a la fecha de inicio.',
400
);
}
const court = await db.court.findFirst({
where: {
id: input.courtId,
complexId,
},
include: {
availabilities: {
where: {
dayOfWeek,
},
orderBy: {
startTime: 'asc',
},
},
sport: {
select: {
id: true,
name: true,
slug: true,
},
},
priceRules: {
where: { isActive: true },
orderBy: [{ dayOfWeek: 'asc' }, { startTime: 'asc' }],
},
},
});
if (!court) {
throw new AdminBookingServiceError('La cancha seleccionada no existe en el complejo.', 404);
}
const validSlots = buildSlots(court.availabilities, court.slotDurationMinutes);
const selectedEndMinutes = toMinutes(input.startTime) + court.slotDurationMinutes;
const selectedSlot = {
startTime: input.startTime,
endTime: minutesToTime(selectedEndMinutes),
};
const slotExists = validSlots.some(
(slot) => slot.startTime === selectedSlot.startTime && slot.endTime === selectedSlot.endTime
);
if (!slotExists) {
throw new AdminBookingServiceError(
'El horario seleccionado no esta disponible para esa cancha.',
409
);
}
if (isSlotInPast(startDate, input.startTime, court.slotDurationMinutes)) {
throw new AdminBookingServiceError('La fecha de inicio no puede estar en el pasado.', 400);
}
const recurringDates = Array.from(
generateRecurringDates(startDate, endDate, startDate.getUTCDay())
);
if (recurringDates.length === 0) {
throw new AdminBookingServiceError(
'No se generaron fechas para la reserva periódica. Verifica las fechas ingresadas.',
400
);
}
for (let attempt = 0; attempt < 5; attempt += 1) {
try {
const result = await db.$transaction(async (tx) => {
const groupId = uuidv7();
const bookingsForDate = await tx.courtBooking.count({
where: {
startTime: selectedSlot.startTime,
bookingDate: startDate,
status: 'CONFIRMED',
court: {
complexId,
},
},
});
const courtsCount = await tx.court.count({
where: { complexId },
});
const violations = evaluatePlanUsage(rules, {
courtsCount,
bookingsToday: bookingsForDate,
});
const maxBookingsViolation = violations.find(
(v) => v.code === 'MAX_BOOKINGS_PER_DAY_REACHED'
);
if (maxBookingsViolation) {
throw new AdminBookingServiceError(maxBookingsViolation.message, 409);
}
const group = await tx.recurringBookingGroup.create({
data: {
id: groupId,
complexId,
courtId: court.id,
startTime: selectedSlot.startTime,
endTime: selectedSlot.endTime,
dayOfWeek,
startDate,
endDate,
status: 'ACTIVE',
customerName: input.customerName.trim(),
customerPhone: input.customerPhone.trim(),
customerEmail: input.customerEmail?.trim() ?? '',
},
});
const createdBookings = [];
for (const date of recurringDates) {
if (isSlotInPast(date, input.startTime, court.slotDurationMinutes)) {
continue;
}
const overlappingBooking = await tx.courtBooking.findFirst({
where: {
courtId: court.id,
bookingDate: date,
status: 'CONFIRMED',
startTime: {
lt: selectedSlot.endTime,
},
endTime: {
gt: selectedSlot.startTime,
},
},
});
if (overlappingBooking) {
throw new AdminBookingServiceError(
`El horario seleccionado ya fue reservado para el dia ${formatIsoDate(date)}.`,
409
);
}
const booking = await tx.courtBooking.create({
data: {
id: uuidv7(),
bookingCode: generateBookingCode(),
courtId: court.id,
bookingDate: date,
startTime: selectedSlot.startTime,
endTime: selectedSlot.endTime,
customerName: input.customerName.trim(),
customerPhone: input.customerPhone.trim(),
customerEmail: input.customerEmail?.trim() ?? '',
status: 'CONFIRMED',
recurringGroupId: groupId,
},
include: {
court: {
select: {
id: true,
name: true,
sport: {
select: {
id: true,
name: true,
slug: true,
},
},
complex: {
select: {
id: true,
complexName: true,
},
},
},
},
},
});
createdBookings.push(booking);
}
return { group, bookings: createdBookings };
});
return {
id: result.group.id,
complexId,
courtId: court.id,
startTime: result.group.startTime,
endTime: result.group.endTime,
dayOfWeek: result.group.dayOfWeek,
startDate: formatIsoDate(result.group.startDate),
endDate: result.group.endDate ? formatIsoDate(result.group.endDate) : null,
status: 'ACTIVE' as const,
customerName: result.group.customerName,
customerPhone: result.group.customerPhone,
customerEmail: result.group.customerEmail,
bookings: result.bookings.map((b) => mapBookingResponse(b)),
createdAt: result.group.createdAt.toISOString(),
updatedAt: result.group.updatedAt.toISOString(),
};
} catch (error) {
if (error instanceof AdminBookingServiceError) {
throw error;
}
const prismaError = error as {
code?: string;
meta?: {
target?: string[] | string;
};
};
if (prismaError.code === 'P2002') {
const targets = Array.isArray(prismaError.meta?.target)
? prismaError.meta?.target
: [prismaError.meta?.target];
const isBookingCodeCollision = targets.some((target) =>
String(target).includes('booking_code')
);
if (isBookingCodeCollision) {
continue;
}
throw new AdminBookingServiceError('El horario seleccionado ya fue reservado.', 409);
}
throw error;
}
}
throw new AdminBookingServiceError(
'No se pudo generar un codigo de reserva unico. Intenta nuevamente.',
409
);
}
export async function cancelRecurringGroup(userId: string, groupId: string) {
const group = await db.recurringBookingGroup.findUnique({
where: { id: groupId },
include: {
complex: {
select: {
id: true,
users: {
where: { userId },
select: { userId: true },
},
},
},
},
});
if (!group) {
throw new AdminBookingServiceError('Grupo de reservas no encontrado.', 404);
}
if (group.complex.users.length === 0) {
throw new AdminBookingServiceError('No tienes permisos para administrar este complejo.', 403);
}
if (group.status === 'CANCELLED') {
throw new AdminBookingServiceError('El grupo ya fue cancelado anteriormente.', 409);
}
const now = new Date();
const todayStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
await db.$transaction(async (tx) => {
await tx.recurringBookingGroup.update({
where: { id: groupId },
data: { status: 'CANCELLED' },
});
const futureBookings = await tx.courtBooking.findMany({
where: {
recurringGroupId: groupId,
bookingDate: { gte: todayStart },
status: 'CONFIRMED',
},
});
for (const booking of futureBookings) {
await tx.courtBookingLog.create({
data: {
id: uuidv7(),
bookingCode: booking.bookingCode,
courtId: booking.courtId,
bookingDate: booking.bookingDate,
startTime: booking.startTime,
endTime: booking.endTime,
customerName: booking.customerName,
customerPhone: booking.customerPhone,
customerEmail: booking.customerEmail,
previousStatus: booking.status,
newStatus: 'CANCELLED',
changedAt: new Date(),
},
});
await tx.courtBooking.delete({
where: { id: booking.id },
});
}
});
return { ok: true };
}

View File

@@ -1,5 +1,6 @@
import { Prisma } from '@/generated/prisma/client';
import { db } from '@/lib/prisma';
import { parsePlanRules } from '@/modules/plan/services/plan-rules.service';
import { v7 as uuidv7 } from 'uuid';
export type CreateComplexInput = {
@@ -185,7 +186,11 @@ export async function listMyComplexes(userId: string) {
const complexUsers = await db.complexUser.findMany({
where: { userId },
include: {
complex: true,
complex: {
include: {
plan: true,
},
},
},
orderBy: {
createdAt: 'asc',
@@ -195,6 +200,9 @@ export async function listMyComplexes(userId: string) {
return complexUsers.map((complexUser) => ({
...complexUser.complex,
role: complexUser.role,
planFeatures: complexUser.complex.plan
? parsePlanRules(complexUser.complex.plan.rules).features
: null,
}));
}
@@ -207,7 +215,11 @@ export async function getCurrentComplex(userId: string, complexId: string) {
},
},
include: {
complex: true,
complex: {
include: {
plan: true,
},
},
},
});
@@ -216,6 +228,9 @@ export async function getCurrentComplex(userId: string, complexId: string) {
return {
...complexUser.complex,
role: complexUser.role,
planFeatures: complexUser.complex.plan
? parsePlanRules(complexUser.complex.plan.rules).features
: null,
};
}
@@ -228,7 +243,11 @@ export async function selectComplex(userId: string, complexId: string) {
},
},
include: {
complex: true,
complex: {
include: {
plan: true,
},
},
},
});
@@ -237,6 +256,9 @@ export async function selectComplex(userId: string, complexId: string) {
return {
...complexUser.complex,
role: complexUser.role,
planFeatures: complexUser.complex.plan
? parsePlanRules(complexUser.complex.plan.rules).features
: null,
};
}

View File

@@ -14,6 +14,7 @@ function makeV1Rules(): PlanRules {
publicBookingPage: false,
advancedReports: false,
whatsappReminders: false,
fixedSlots: false,
},
};
}
@@ -28,6 +29,7 @@ function makeV2Rules(overrides?: Record<string, { amount: number; currency: stri
publicBookingPage: true,
advancedReports: true,
whatsappReminders: true,
fixedSlots: true,
},
...(overrides ? { pricing: { overrides } } : {}),
};

View File

@@ -50,6 +50,7 @@ type PlanFormRules = {
publicBookingPage: boolean;
advancedReports: boolean;
whatsappReminders: boolean;
fixedSlots: boolean;
};
policies: {
maxAdvanceBookingDays: number | '';
@@ -85,6 +86,7 @@ function getDefaultRules(): PlanFormRules {
publicBookingPage: false,
advancedReports: false,
whatsappReminders: false,
fixedSlots: false,
},
policies: {
maxAdvanceBookingDays: '',
@@ -113,6 +115,7 @@ function initRules(plan?: Plan): PlanFormRules {
publicBookingPage: (features?.publicBookingPage as boolean) ?? false,
advancedReports: (features?.advancedReports as boolean) ?? false,
whatsappReminders: (features?.whatsappReminders as boolean) ?? false,
fixedSlots: (features?.fixedSlots as boolean) ?? false,
},
policies: {
maxAdvanceBookingDays: (policies?.maxAdvanceBookingDays as number | undefined) ?? '',
@@ -437,6 +440,7 @@ function PlanFormDialog({
['publicBookingPage', 'Página de reservas pública'],
['advancedReports', 'Reportes avanzados'],
['whatsappReminders', 'Recordatorios por WhatsApp'],
['fixedSlots', 'Turnos fijos'],
] as const
).map(([key, label]) => (
<div key={key} className="flex items-center justify-between">

View File

@@ -1,5 +1,5 @@
import { ApiClientError, apiClient } from '@/lib/api-client';
import type { AdminBooking, ComplexWithRole, Court } from '@repo/api-contract';
import type { AdminBooking, ComplexWithRole, Court, PlanFeatureFlags } from '@repo/api-contract';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
type ReactNode,
@@ -44,6 +44,16 @@ interface CreateManualBookingPayload {
customerEmail: string;
}
interface CreateRecurringBookingPayload {
courtId: string;
date: string;
startTime: string;
customerName: string;
customerPhone: string;
customerEmail: string;
recurringEndDate?: string;
}
interface BookingContextValue {
complex: ComplexWithRole;
courts: Court[];
@@ -66,6 +76,8 @@ interface BookingContextValue {
createBookingError: string | null;
isCreatingBooking: boolean;
bookingToolsOpen: boolean;
planFeatures: PlanFeatureFlags | null;
isRecurringEnabled: boolean;
setSelectedDate: (date: string) => void;
moveSelectedDate: (amount: number) => void;
setSelectedSportId: (sportId: string) => void;
@@ -75,6 +87,7 @@ interface BookingContextValue {
openCreateBooking: (draft?: Partial<BookingSlotDraft>) => void;
closeCreateBooking: () => void;
createBooking: (payload: CreateManualBookingPayload) => Promise<void>;
createRecurringBooking: (payload: CreateRecurringBookingPayload) => Promise<void>;
updateBookingStatus: (bookingId: string, status: 'CANCELLED' | 'COMPLETED' | 'NOSHOW') => void;
exportDayReport: () => void;
openBookingTools: (segment?: BookingTimelineSegment) => void;
@@ -325,6 +338,26 @@ export function BookingProvider({ children, complex }: BookingProviderProps) {
},
});
const createRecurringBookingMutation = useMutation({
mutationFn: (payload: CreateRecurringBookingPayload) =>
apiClient.adminBookings.createRecurring(complex.id, {
date: payload.date,
courtId: payload.courtId,
startTime: payload.startTime,
customerName: payload.customerName,
customerPhone: payload.customerPhone,
customerEmail: payload.customerEmail,
isRecurring: true,
recurringEndDate: payload.recurringEndDate,
}),
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ['admin-bookings', complex.id] });
await queryClient.invalidateQueries({ queryKey: ['manual-booking-availability'] });
setIsCreateBookingOpen(false);
setSelectedSlot(null);
},
});
const updateStatusMutation = useMutation({
mutationFn: (payload: { bookingId: string; status: 'CANCELLED' | 'COMPLETED' | 'NOSHOW' }) =>
apiClient.adminBookings.updateStatus(payload.bookingId, { status: payload.status }),
@@ -413,6 +446,16 @@ export function BookingProvider({ children, complex }: BookingProviderProps) {
URL.revokeObjectURL(url);
}, [bookings, complex.complexSlug, selectedDate]);
const planFeatures = complex.planFeatures ?? null;
const isRecurringEnabled = planFeatures?.fixedSlots ?? false;
const createRecurringBooking = useCallback(
async (payload: CreateRecurringBookingPayload) => {
await createRecurringBookingMutation.mutateAsync(payload);
},
[createRecurringBookingMutation]
);
const value = useMemo<BookingContextValue>(
() => ({
complex,
@@ -438,10 +481,17 @@ export function BookingProvider({ children, complex }: BookingProviderProps) {
)
: null,
isCreateBookingOpen,
createBookingError: createBookingMutation.isError
? extractMessage(createBookingMutation.error, 'No pudimos crear la reserva.')
: null,
isCreatingBooking: createBookingMutation.isPending,
createBookingError:
createBookingMutation.isError || createRecurringBookingMutation.isError
? extractMessage(
createBookingMutation.error ?? createRecurringBookingMutation.error,
'No pudimos crear la reserva.'
)
: null,
isCreatingBooking:
createBookingMutation.isPending || createRecurringBookingMutation.isPending,
planFeatures,
isRecurringEnabled,
setSelectedDate,
moveSelectedDate: (amount) => setSelectedDate((date) => addDaysIso(date, amount)),
setSelectedSportId,
@@ -451,6 +501,7 @@ export function BookingProvider({ children, complex }: BookingProviderProps) {
openCreateBooking,
closeCreateBooking,
createBooking,
createRecurringBooking,
updateBookingStatus,
exportDayReport,
bookingToolsOpen,
@@ -473,10 +524,16 @@ export function BookingProvider({ children, complex }: BookingProviderProps) {
createBookingMutation.error,
createBookingMutation.isError,
createBookingMutation.isPending,
createRecurringBooking,
createRecurringBookingMutation.error,
createRecurringBookingMutation.isError,
createRecurringBookingMutation.isPending,
currentTime,
exportDayReport,
isCreateBookingOpen,
openCreateBooking,
planFeatures,
isRecurringEnabled,
schedules,
selectedDate,
selectedSlot,

View File

@@ -18,6 +18,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Separator } from '@/components/ui/separator';
import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
@@ -60,12 +61,16 @@ export function BookingCreateDialog() {
isCreatingBooking,
closeCreateBooking,
createBooking,
createRecurringBooking,
isRecurringEnabled,
} = useBooking();
const [date, setDate] = useState(selectedSlot?.date ?? selectedDate);
const [sportId, setSportId] = useState(selectedSlot?.sportId ?? 'all');
const [courtId, setCourtId] = useState(selectedSlot?.courtId ?? '');
const [startTime, setStartTime] = useState(selectedSlot?.startTime ?? '');
const [isRecurring, setIsRecurring] = useState(false);
const [recurringEndDate, setRecurringEndDate] = useState<string | undefined>(undefined);
const {
register,
@@ -90,6 +95,8 @@ export function BookingCreateDialog() {
setSportId(selectedSlot?.sportId ?? 'all');
setCourtId(selectedSlot?.courtId ?? '');
setStartTime(selectedSlot?.startTime ?? '');
setIsRecurring(false);
setRecurringEndDate(undefined);
reset();
if (selectedSlot?.courtId && selectedSlot?.startTime) {
@@ -155,14 +162,26 @@ export function BookingCreateDialog() {
}, [availableStartTimes, startTime]);
const onSubmit = async (values: BookingForm) => {
await createBooking({
courtId,
date,
startTime,
customerName: values.customerName,
customerPhone: values.customerPhone,
customerEmail: values.customerEmail,
});
if (isRecurring) {
await createRecurringBooking({
courtId,
date,
startTime,
customerName: values.customerName,
customerPhone: values.customerPhone,
customerEmail: values.customerEmail,
recurringEndDate,
});
} else {
await createBooking({
courtId,
date,
startTime,
customerName: values.customerName,
customerPhone: values.customerPhone,
customerEmail: values.customerEmail,
});
}
};
return (
@@ -260,6 +279,52 @@ export function BookingCreateDialog() {
</Field>
</div>
{isRecurringEnabled && (
<>
<Separator />
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<p className="text-sm font-medium">Repetir todas las semanas</p>
<p className="text-xs text-muted-foreground">
{isRecurring
? `Se creará una reserva todos los ${getDayOfWeekLabel(date)} a las ${startTime || '...'}`
: 'Crea turnos fijos en el mismo horario'}
</p>
</div>
<Button
type="button"
variant={isRecurring ? 'default' : 'outline'}
size="sm"
onClick={() => setIsRecurring(!isRecurring)}
className="shrink-0"
>
{isRecurring ? 'Activado' : 'Desactivado'}
</Button>
</div>
{isRecurring && (
<Field>
<FieldLabel>
Repetir hasta{' '}
<span className="text-muted-foreground font-normal">(opcional)</span>
</FieldLabel>
<DatePicker
value={fromIsoDateLocal(recurringEndDate ?? '')}
onChange={(nextDate) => {
setRecurringEndDate(nextDate ? toIsoDateLocal(nextDate) : undefined);
}}
/>
<p className="mt-1 text-xs text-muted-foreground">
Si no se selecciona una fecha, se repetirá por tiempo indefinido.
</p>
</Field>
)}
</>
)}
<Separator />
<Field data-invalid={Boolean(errors.customerName)}>
<FieldLabel htmlFor="customerName">Nombre</FieldLabel>
<Input
@@ -312,10 +377,29 @@ export function BookingCreateDialog() {
form="booking-create-form"
disabled={!isValid || isCreatingBooking || !courtId || !startTime}
>
{isCreatingBooking ? 'Guardando...' : 'Crear reserva'}
{isCreatingBooking
? isRecurring
? 'Creando turnos...'
: 'Guardando...'
: isRecurring
? 'Crear turno fijo'
: 'Crear reserva'}
</Button>
</ResponsiveDialogFooter>
</ResponsiveDialogContent>
</ResponsiveDialog>
);
}
function getDayOfWeekLabel(dateIso: string): string {
const labels: Record<string, string> = {
MONDAY: 'lunes',
TUESDAY: 'martes',
WEDNESDAY: 'miércoles',
THURSDAY: 'jueves',
FRIDAY: 'viernes',
SATURDAY: 'sábado',
SUNDAY: 'domingo',
};
return labels[getDayOfWeek(dateIso)] ?? '';
}

View File

@@ -13,6 +13,7 @@ const FEATURE_LABELS: Record<string, string> = {
onlinePayments: 'Pagos online',
advancedReports: 'Reportes avanzados',
whatsappReminders: 'Recordatorios WhatsApp',
fixedSlots: 'Turnos fijos',
};
export function PlanSelectStep({ form, plans }: PlanSelectStepProps) {
@@ -24,6 +25,7 @@ export function PlanSelectStep({ form, plans }: PlanSelectStepProps) {
'onlinePayments',
'advancedReports',
'whatsappReminders',
'fixedSlots',
] as const;
return (

View File

@@ -74,6 +74,8 @@ export const apiClient = {
adminBookings: {
listByComplex: api.listByComplex,
create: api.createAdmin,
createRecurring: api.createRecurring,
cancelRecurringGroup: api.cancelRecurringGroup,
updateStatus: api.updateStatus,
},
};

View File

@@ -15,5 +15,7 @@ export {
getConfirmation,
listByComplex,
createAdmin,
createRecurring,
cancelRecurringGroup,
updateStatus,
} from './resources/bookings';

View File

@@ -6,8 +6,10 @@ import type {
PublicAvailabilityResponse,
PublicBooking,
PublicBookingConfirmation,
RecurringBookingGroup,
UpdateAdminBookingStatusInput,
} from '@repo/api-contract';
import type { CreateRecurringBookingInput } from '@repo/api-contract';
import { http } from '../http';
export async function getAvailability(
@@ -52,6 +54,21 @@ export async function createAdmin(complexId: string, payload: CreateAdminBooking
return response.data;
}
export async function createRecurring(complexId: string, payload: CreateRecurringBookingInput) {
const response = await http.post<RecurringBookingGroup>(
`/api/admin-bookings/complex/${complexId}/recurring`,
payload
);
return response.data;
}
export async function cancelRecurringGroup(groupId: string) {
const response = await http.post<{ ok: boolean }>(
`/api/admin-bookings/recurring/${groupId}/cancel`
);
return response.data;
}
export async function cancelPublic(complexSlug: string, payload: CancelPublicBookingInput) {
const response = await http.post<PublicBooking>(
`/api/public-bookings/complex/${complexSlug}/cancel`,