feat: add error handling to player creation and update gitignore for agent

This commit is contained in:
Erik Silva
2026-01-24 14:18:40 -03:00
commit 416bd83ea7
93 changed files with 16861 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
-- CreateEnum
CREATE TYPE "MatchStatus" AS ENUM ('IN_PROGRESS', 'COMPLETED');
-- CreateTable
CREATE TABLE "Group" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"logoUrl" TEXT,
"primaryColor" TEXT NOT NULL DEFAULT '#000000',
"secondaryColor" TEXT NOT NULL DEFAULT '#ffffff',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Group_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Player" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"number" INTEGER NOT NULL,
"position" TEXT NOT NULL DEFAULT 'MEI',
"level" INTEGER NOT NULL DEFAULT 3,
"groupId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Player_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Sponsor" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"logoUrl" TEXT,
"groupId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Sponsor_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Match" (
"id" TEXT NOT NULL,
"date" TIMESTAMP(3) NOT NULL,
"status" "MatchStatus" NOT NULL DEFAULT 'IN_PROGRESS',
"groupId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Match_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Team" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"color" TEXT NOT NULL,
"matchId" TEXT NOT NULL,
CONSTRAINT "Team_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TeamPlayer" (
"id" TEXT NOT NULL,
"teamId" TEXT NOT NULL,
"playerId" TEXT NOT NULL,
CONSTRAINT "TeamPlayer_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Player_number_groupId_key" ON "Player"("number", "groupId");
-- AddForeignKey
ALTER TABLE "Player" ADD CONSTRAINT "Player_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Sponsor" ADD CONSTRAINT "Sponsor_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Match" ADD CONSTRAINT "Match_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Team" ADD CONSTRAINT "Team_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TeamPlayer" ADD CONSTRAINT "TeamPlayer_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TeamPlayer" ADD CONSTRAINT "TeamPlayer_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,15 @@
/*
Warnings:
- A unique constraint covering the columns `[email]` on the table `Group` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "Group" ADD COLUMN "email" TEXT,
ADD COLUMN "password" TEXT;
-- AlterTable
ALTER TABLE "Player" ALTER COLUMN "number" DROP NOT NULL;
-- CreateIndex
CREATE UNIQUE INDEX "Group_email_key" ON "Group"("email");

View File

@@ -0,0 +1,24 @@
-- AlterTable
ALTER TABLE "Match" ADD COLUMN "drawSeed" TEXT,
ADD COLUMN "location" TEXT,
ADD COLUMN "maxPlayers" INTEGER;
-- CreateTable
CREATE TABLE "Attendance" (
"id" TEXT NOT NULL,
"playerId" TEXT NOT NULL,
"matchId" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'CONFIRMED',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Attendance_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Attendance_playerId_matchId_key" ON "Attendance"("playerId", "matchId");
-- AddForeignKey
ALTER TABLE "Attendance" ADD CONSTRAINT "Attendance_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Attendance" ADD CONSTRAINT "Attendance_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,10 @@
-- AlterEnum
-- This migration adds more than one value to an enum.
-- With PostgreSQL versions 11 and earlier, this is not possible
-- in a single migration. This can be worked around by creating
-- multiple migrations, each migration adding only one value to
-- the enum.
ALTER TYPE "MatchStatus" ADD VALUE 'SCHEDULED';
ALTER TYPE "MatchStatus" ADD VALUE 'CANCELED';

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Match" ALTER COLUMN "status" SET DEFAULT 'SCHEDULED';

View File

@@ -0,0 +1,17 @@
-- DropForeignKey
ALTER TABLE "Attendance" DROP CONSTRAINT "Attendance_matchId_fkey";
-- DropForeignKey
ALTER TABLE "Team" DROP CONSTRAINT "Team_matchId_fkey";
-- DropForeignKey
ALTER TABLE "TeamPlayer" DROP CONSTRAINT "TeamPlayer_teamId_fkey";
-- AddForeignKey
ALTER TABLE "Attendance" ADD CONSTRAINT "Attendance_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Team" ADD CONSTRAINT "Team_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TeamPlayer" ADD CONSTRAINT "TeamPlayer_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Match" ADD COLUMN "isRecurring" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "recurrenceInterval" TEXT;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Match" ADD COLUMN "recurrenceEndDate" TIMESTAMP(3);

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

206
prisma/schema.prisma Normal file
View File

@@ -0,0 +1,206 @@
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "debian-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Super Admin da plataforma TemFut (acessa admin.localhost)
model Admin {
id String @id @default(cuid())
email String @unique
password String
name String
role AdminRole @default(ADMIN)
createdAt DateTime @default(now())
}
enum AdminRole {
SUPER_ADMIN // Acesso total
ADMIN // Acesso de leitura
}
model Group {
id String @id @default(cuid())
slug String @unique @default(cuid()) // Para subdomínios (ex: flamengo.temfut.com)
name String
logoUrl String?
primaryColor String @default("#000000")
secondaryColor String @default("#ffffff")
createdAt DateTime @default(now())
email String? @unique
password String?
plan Plan @default(FREE)
planExpiresAt DateTime?
matches Match[]
players Player[]
sponsors Sponsor[]
arenas Arena[]
financialEvents FinancialEvent[]
pixKey String?
pixName String?
status GroupStatus @default(ACTIVE)
}
enum GroupStatus {
ACTIVE
FROZEN
}
enum Plan {
FREE
BASIC
PRO
}
model Player {
id String @id @default(cuid())
name String
number Int?
position String @default("MEI")
level Int @default(3)
groupId String
createdAt DateTime @default(now())
group Group @relation(fields: [groupId], references: [id])
teams TeamPlayer[]
attendances Attendance[]
payments Payment[]
@@unique([number, groupId])
}
model Attendance {
id String @id @default(cuid())
playerId String
player Player @relation(fields: [playerId], references: [id])
matchId String
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
status String @default("CONFIRMED") // CONFIRMED, CANCELED
createdAt DateTime @default(now())
@@unique([playerId, matchId])
}
model Match {
id String @id @default(cuid())
date DateTime
location String?
arenaId String?
arena Arena? @relation(fields: [arenaId], references: [id])
maxPlayers Int?
drawSeed String?
status MatchStatus @default(SCHEDULED)
groupId String
createdAt DateTime @default(now())
group Group @relation(fields: [groupId], references: [id])
teams Team[]
attendances Attendance[]
isRecurring Boolean @default(false)
recurrenceInterval String? // 'WEEKLY'
recurrenceEndDate DateTime?
}
model Team {
id String @id @default(cuid())
name String
color String
matchId String
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
players TeamPlayer[]
}
model TeamPlayer {
id String @id @default(cuid())
teamId String
playerId String
player Player @relation(fields: [playerId], references: [id])
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
}
enum MatchStatus {
SCHEDULED
IN_PROGRESS
COMPLETED
CANCELED
}
model Sponsor {
id String @id @default(cuid())
name String
logoUrl String?
groupId String
createdAt DateTime @default(now())
group Group @relation(fields: [groupId], references: [id])
}
model Arena {
id String @id @default(cuid())
name String
address String?
groupId String
group Group @relation(fields: [groupId], references: [id])
matches Match[]
createdAt DateTime @default(now())
}
model FinancialEvent {
id String @id @default(cuid())
title String
description String?
totalAmount Float? // Meta de arrecadação (opcional)
pricePerPerson Float? // Valor por pessoa
dueDate DateTime
type FinancialEventType @default(MONTHLY_FEE)
status FinancialEventStatus @default(OPEN)
groupId String
group Group @relation(fields: [groupId], references: [id])
payments Payment[]
createdAt DateTime @default(now())
isRecurring Boolean @default(false)
recurrenceInterval String? // 'MONTHLY', 'WEEKLY'
recurrenceEndDate DateTime?
}
model Payment {
id String @id @default(cuid())
financialEventId String
financialEvent FinancialEvent @relation(fields: [financialEventId], references: [id], onDelete: Cascade)
playerId String
player Player @relation(fields: [playerId], references: [id])
amount Float
status PaymentStatus @default(PENDING)
paidAt DateTime?
method String? // PIX, CASH
createdAt DateTime @default(now())
@@unique([financialEventId, playerId])
}
enum FinancialEventType {
MONTHLY_FEE
EXTRA_EVENT
CONTRIBUTION
}
enum FinancialEventStatus {
OPEN
CLOSED
CANCELED
}
enum PaymentStatus {
PENDING
PAID
WAIVED
}

47
prisma/seed.ts Normal file
View File

@@ -0,0 +1,47 @@
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcryptjs'
const prisma = new PrismaClient()
async function main() {
const password = await bcrypt.hash('Android@2020', 12)
// 1. Criar Admin
await prisma.admin.upsert({
where: { email: 'admin@temfut.com' },
update: {},
create: {
email: 'admin@temfut.com',
name: 'Super Admin',
password: password,
role: 'SUPER_ADMIN'
}
})
// 2. Criar Pelada de Exemplo
await prisma.group.upsert({
where: { email: 'erik@idealpages.com.br' },
update: {},
create: {
name: 'Fut de Quarta',
slug: 'futdequarta',
email: 'erik@idealpages.com.br',
password: password,
status: 'ACTIVE',
plan: 'PRO',
primaryColor: '#10b981',
secondaryColor: '#000000'
}
})
console.log('Seed concluído com sucesso!')
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})