From aa418b7872605b09ee6610801194d437b652e76f Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 1 Feb 2026 04:19:11 +0000 Subject: [PATCH] docs: add Padel Pro system design document Complete design specification including: - Multi-site architecture for padel club management - Booking system with client app and admin panel - Point of sale (POS) with inventory - Tournaments and leagues module - Membership plans with benefits - Full database schema (Prisma) - UI/UX guidelines and color palette - Project structure (monorepo with Turborepo) Co-Authored-By: Claude Opus 4.5 --- docs/plans/2026-02-01-padel-pro-design.md | 560 ++++++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 docs/plans/2026-02-01-padel-pro-design.md diff --git a/docs/plans/2026-02-01-padel-pro-design.md b/docs/plans/2026-02-01-padel-pro-design.md new file mode 100644 index 0000000..36232e8 --- /dev/null +++ b/docs/plans/2026-02-01-padel-pro-design.md @@ -0,0 +1,560 @@ +# Padel Pro - Documento de Diseño + +**Fecha:** 2026-02-01 +**Estado:** Aprobado +**Versión:** 1.0 + +--- + +## Resumen Ejecutivo + +Sistema integral de gestión para cadena de clubes de pádel con múltiples sedes. Incluye gestión de reservas, punto de venta, torneos/ligas y membresías. + +### Alcance + +| Aspecto | Decisión | +|---------|----------| +| **Tipo de usuario** | Dueño de club/canchas | +| **Escala** | Múltiples sedes | +| **Modelo de negocio** | Mixto (reservas por hora + membresías) | +| **Funcionalidades** | Reservas, Torneos/Ligas, Punto de Venta, Membresías | +| **Pagos** | Efectivo, transferencias, terminal física | +| **Plataformas** | Web (admin) + App móvil (clientes) | + +--- + +## Arquitectura + +### Enfoque: Monolito Moderno + +``` +┌─────────────────────────────────────────┐ +│ Next.js (Web + API) │ +│ ┌─────────┐ ┌─────────┐ ┌───────────┐ │ +│ │ Admin │ │ API │ │ Auth │ │ +│ │ Panel │ │ Routes │ │ (NextAuth)│ │ +│ └─────────┘ └─────────┘ └───────────┘ │ +└─────────────────────────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────┐ ┌───────────────────┐ +│ PostgreSQL │ │ React Native App │ +│ (Prisma ORM) │ │ (Clientes) │ +└─────────────────┘ └───────────────────┘ +``` + +### Stack Técnico + +| Capa | Tecnología | +|------|------------| +| **Frontend Web** | Next.js 14, TypeScript, Tailwind CSS, shadcn/ui, React Query | +| **Backend/API** | Next.js API Routes, Prisma ORM, NextAuth.js, Zod | +| **Base de datos** | PostgreSQL | +| **Mobile** | React Native, Expo, TypeScript, NativeWind | +| **Infraestructura** | Vercel/VPS, Railway/Supabase (DB), Cloudinary | + +--- + +## Módulos del Sistema + +``` +┌────────────────────────────────────────────────────────────┐ +│ PADEL PRO - MÓDULOS │ +├──────────────┬──────────────┬──────────────┬───────────────┤ +│ RESERVAS │ TORNEOS │ POS │ MEMBRESÍAS │ +│ │ │ │ │ +│ • Calendario │ • Crear │ • Productos │ • Planes │ +│ • Disponib. │ torneos │ • Ventas │ • Beneficios │ +│ • Booking │ • Brackets │ • Caja │ • Renovación │ +│ • Bloqueos │ • Rankings │ • Cortes │ • Historial │ +└──────────────┴──────────────┴──────────────┴───────────────┘ + │ + ┌───────────────┴───────────────┐ + │ CORE (Base) │ + │ • Multi-sede │ + │ • Usuarios y roles │ + │ • Clientes │ + │ • Pagos y caja │ + │ • Reportes │ + └───────────────────────────────┘ +``` + +--- + +## Roles de Usuario + +| Rol | Permisos | +|-----|----------| +| **Super Admin** | Todas las sedes, reportes consolidados, configuración global | +| **Admin de Sede** | Gestiona una sede específica, reservas, caja y personal | +| **Recepcionista** | Opera reservas, cobra, registra pagos, atiende clientes | +| **Cliente (App)** | Reserva canchas, ve partidos, inscribe a torneos | + +--- + +## Modelo de Datos + +### Entidades Core + +```prisma +// Organización y Sedes +model Organization { + id String @id @default(cuid()) + name String + logo String? + sites Site[] + users User[] + clients Client[] + createdAt DateTime @default(now()) +} + +model Site { + id String @id @default(cuid()) + organizationId String + organization Organization @relation(fields: [organizationId], references: [id]) + name String + address String + phone String? + openTime String // "08:00" + closeTime String // "22:00" + courts Court[] + users User[] + products Product[] + tournaments Tournament[] + createdAt DateTime @default(now()) +} + +model Court { + id String @id @default(cuid()) + siteId String + site Site @relation(fields: [siteId], references: [id]) + name String + type CourtType @default(DOUBLES) + pricePerHour Decimal + premiumPrice Decimal? // Precio horario premium + status CourtStatus @default(ACTIVE) + bookings Booking[] +} + +enum CourtType { + SINGLES + DOUBLES + MIXED +} + +enum CourtStatus { + ACTIVE + MAINTENANCE + INACTIVE +} + +// Usuarios y Clientes +model User { + id String @id @default(cuid()) + organizationId String + organization Organization @relation(fields: [organizationId], references: [id]) + siteId String? + site Site? @relation(fields: [siteId], references: [id]) + email String @unique + password String + name String + phone String? + role UserRole + createdAt DateTime @default(now()) +} + +enum UserRole { + SUPER_ADMIN + SITE_ADMIN + RECEPTIONIST +} + +model Client { + id String @id @default(cuid()) + organizationId String + organization Organization @relation(fields: [organizationId], references: [id]) + email String @unique + password String + name String + phone String? + photo String? + balance Decimal @default(0) + membership Membership? + bookings Booking[] + createdAt DateTime @default(now()) +} + +// Reservas +model Booking { + id String @id @default(cuid()) + courtId String + court Court @relation(fields: [courtId], references: [id]) + clientId String + client Client @relation(fields: [clientId], references: [id]) + date DateTime + startTime String // "10:00" + endTime String // "11:00" + price Decimal + status BookingStatus @default(PENDING) + paymentType PaymentType? + isPaid Boolean @default(false) + notes String? + payments Payment[] + createdAt DateTime @default(now()) + createdBy String? // User ID si fue creada por staff +} + +enum BookingStatus { + PENDING + CONFIRMED + CANCELLED + COMPLETED +} + +enum PaymentType { + CASH + TRANSFER + CARD_TERMINAL +} + +// Pagos +model Payment { + id String @id @default(cuid()) + amount Decimal + method PaymentType + reference String? + bookingId String? + booking Booking? @relation(fields: [bookingId], references: [id]) + saleId String? + sale Sale? @relation(fields: [saleId], references: [id]) + createdBy String // User ID + createdAt DateTime @default(now()) +} + +// Membresías +model MembershipPlan { + id String @id @default(cuid()) + organizationId String + name String + price Decimal + freeHours Int + bookingDiscount Int // Porcentaje + storeDiscount Int // Porcentaje + extraBenefits String? + memberships Membership[] +} + +model Membership { + id String @id @default(cuid()) + clientId String @unique + client Client @relation(fields: [clientId], references: [id]) + planId String + plan MembershipPlan @relation(fields: [planId], references: [id]) + startDate DateTime + endDate DateTime + hoursUsed Int @default(0) + status MembershipStatus @default(ACTIVE) +} + +enum MembershipStatus { + ACTIVE + EXPIRED + CANCELLED +} + +// Punto de Venta +model ProductCategory { + id String @id @default(cuid()) + name String + products Product[] +} + +model Product { + id String @id @default(cuid()) + siteId String + site Site @relation(fields: [siteId], references: [id]) + categoryId String + category ProductCategory @relation(fields: [categoryId], references: [id]) + name String + price Decimal + stock Int @default(0) + minStock Int @default(5) + saleItems SaleItem[] +} + +model Sale { + id String @id @default(cuid()) + siteId String + items SaleItem[] + total Decimal + payments Payment[] + createdBy String + createdAt DateTime @default(now()) +} + +model SaleItem { + id String @id @default(cuid()) + saleId String + sale Sale @relation(fields: [saleId], references: [id]) + productId String + product Product @relation(fields: [productId], references: [id]) + quantity Int + price Decimal +} + +// Control de Caja +model CashRegister { + id String @id @default(cuid()) + siteId String + openedBy String + closedBy String? + openingAmount Decimal + closingAmount Decimal? + expectedAmount Decimal? + openedAt DateTime @default(now()) + closedAt DateTime? + status CashRegisterStatus @default(OPEN) +} + +enum CashRegisterStatus { + OPEN + CLOSED +} + +// Torneos +model Tournament { + id String @id @default(cuid()) + siteId String + site Site @relation(fields: [siteId], references: [id]) + name String + description String? + date DateTime + endDate DateTime? + type TournamentType + category String? // A, B, C o null si no aplica + maxTeams Int + price Decimal + status TournamentStatus @default(DRAFT) + inscriptions TournamentInscription[] + matches Match[] +} + +enum TournamentType { + SINGLE_ELIMINATION + DOUBLE_ELIMINATION + ROUND_ROBIN + LEAGUE +} + +enum TournamentStatus { + DRAFT + OPEN + IN_PROGRESS + FINISHED + CANCELLED +} + +model TournamentInscription { + id String @id @default(cuid()) + tournamentId String + tournament Tournament @relation(fields: [tournamentId], references: [id]) + player1Id String + player2Id String? // Para parejas + teamName String? + createdAt DateTime @default(now()) +} + +model Match { + id String @id @default(cuid()) + tournamentId String + tournament Tournament @relation(fields: [tournamentId], references: [id]) + round Int + position Int + team1Id String? + team2Id String? + score1 String? + score2 String? + winnerId String? + courtId String? + scheduledAt DateTime? + status MatchStatus @default(PENDING) +} + +enum MatchStatus { + PENDING + IN_PROGRESS + FINISHED +} +``` + +--- + +## Flujos Principales + +### Flujo de Reserva - Cliente (App) + +1. Cliente abre app y selecciona sede +2. Elige fecha en calendario +3. Ve horarios disponibles con precios +4. Selecciona horario y confirma +5. Reserva queda como PENDIENTE +6. Al llegar, staff cobra y marca como PAGADA + +### Flujo de Reserva - Recepcionista (Web) + +1. Ve calendario visual de canchas +2. Click en slot disponible +3. Busca cliente existente o crea nuevo +4. Si cliente presente: cobra y confirma +5. Si no presente: reserva pendiente + +### Flujo de Venta POS + +1. Recepcionista abre pantalla de caja +2. Agrega productos al ticket +3. Opcionalmente agrega reserva al mismo ticket +4. Selecciona método de pago +5. Registra pago y cierra venta + +### Flujo de Torneo + +1. Admin crea torneo (nombre, fecha, categoría, precio) +2. Abre inscripciones +3. Clientes se inscriben desde app +4. Admin cierra inscripciones y genera bracket +5. Asigna horarios y canchas +6. Registra resultados de partidos +7. Sistema avanza bracket automáticamente + +--- + +## Diseño Visual + +### Paleta de Colores + +| Uso | Color | Hex | +|-----|-------|-----| +| Primario | Azul profundo | #1E3A5F | +| Acento | Verde pádel | #22C55E | +| Fondo | Blanco/Gris claro | #F8FAFC | +| Texto | Gris oscuro | #1E293B | +| Éxito | Verde | #22C55E | +| Alerta | Ámbar | #F59E0B | +| Error | Rojo suave | #EF4444 | + +### Principios de Diseño + +1. **Minimalista**: Solo información esencial en pantalla +2. **Touch-friendly**: Botones grandes en móvil (mínimo 44px) +3. **Feedback visual**: Confirmaciones claras, estados visibles +4. **Responsive**: Admin funciona en desktop y tablet +5. **Modo oscuro**: Opcional para ambas plataformas +6. **Consistencia**: Mismos patrones en web y móvil + +### Componentes UI (shadcn/ui) + +- Calendario de reservas (vista día/semana) +- Cards para estadísticas +- Tablas con filtros y búsqueda +- Modales para formularios +- Toast notifications +- Sidebar navegación colapsable + +--- + +## Estructura del Proyecto + +``` +padel-pro/ +├── apps/ +│ ├── web/ # Next.js (Admin + API) +│ │ ├── app/ +│ │ │ ├── (admin)/ # Panel administrativo +│ │ │ │ ├── dashboard/ +│ │ │ │ ├── bookings/ +│ │ │ │ ├── pos/ +│ │ │ │ ├── tournaments/ +│ │ │ │ ├── clients/ +│ │ │ │ ├── memberships/ +│ │ │ │ ├── reports/ +│ │ │ │ └── settings/ +│ │ │ ├── (auth)/ +│ │ │ │ ├── login/ +│ │ │ │ └── forgot-password/ +│ │ │ └── api/ +│ │ │ ├── auth/ +│ │ │ ├── bookings/ +│ │ │ ├── clients/ +│ │ │ ├── courts/ +│ │ │ ├── products/ +│ │ │ ├── sales/ +│ │ │ ├── tournaments/ +│ │ │ └── reports/ +│ │ ├── components/ +│ │ │ ├── ui/ # shadcn components +│ │ │ ├── booking/ +│ │ │ ├── pos/ +│ │ │ └── layout/ +│ │ └── lib/ +│ │ ├── db.ts +│ │ ├── auth.ts +│ │ └── utils.ts +│ │ +│ └── mobile/ # React Native (Clientes) +│ ├── app/ # Expo Router +│ │ ├── (tabs)/ +│ │ │ ├── index.tsx # Home/Reservar +│ │ │ ├── bookings.tsx # Mis reservas +│ │ │ ├── tournaments.tsx +│ │ │ └── profile.tsx +│ │ ├── (auth)/ +│ │ └── booking/[id].tsx +│ ├── components/ +│ └── services/ +│ └── api.ts +│ +├── packages/ +│ └── shared/ +│ ├── types/ +│ └── validations/ +│ +├── prisma/ +│ ├── schema.prisma +│ └── seed.ts +│ +├── docs/ +│ └── plans/ +│ +├── turbo.json +├── package.json +└── README.md +``` + +--- + +## Consideraciones de Seguridad + +- Autenticación JWT con refresh tokens +- Passwords hasheados con bcrypt +- Validación de inputs con Zod +- Rate limiting en API +- CORS configurado correctamente +- Variables sensibles en environment + +--- + +## Próximos Pasos + +1. Inicializar monorepo con Turborepo +2. Configurar base de datos y Prisma +3. Implementar autenticación +4. Desarrollar módulo de reservas (core) +5. Agregar POS +6. Implementar torneos +7. Desarrollar app móvil +8. Testing y QA +9. Deploy a producción + +--- + +*Documento generado colaborativamente - Febrero 2026*