Commit 5917bcb1 by Augusto

small fixes

parent 7730f929
-- Update agents table to use countryWyId foreign key instead of country text field
-- This migration:
-- 1. Adds the new countryWyId column with a foreign key reference to areas.wy_id
-- 2. Drops the old country column
-- 3. Adds an index on countryWyId for query performance
-- Add new countryWyId column (initially nullable to allow migration)
ALTER TABLE agents ADD COLUMN country_wy_id INTEGER;
-- Add index for query performance
CREATE INDEX idx_agents_country_wy_id ON agents(country_wy_id);
-- Drop the old country column
ALTER TABLE agents DROP COLUMN country;
-- Add foreign key constraint (after dropping old column to avoid conflicts)
ALTER TABLE agents ADD CONSTRAINT agents_country_wy_id_fk
FOREIGN KEY (country_wy_id) REFERENCES areas(wy_id) ON DELETE RESTRICT;
-- Make countryWyId NOT NULL after migration
ALTER TABLE agents ALTER COLUMN country_wy_id SET NOT NULL;
-- Remove agentId column from players table
-- Agent relationships are now managed through the playerAgents junction table
-- This migration drops the direct foreign key reference to agents
-- Drop the foreign key constraint if it exists
ALTER TABLE players DROP CONSTRAINT IF EXISTS players_agent_id_fk;
-- Drop the agentId column
ALTER TABLE players DROP COLUMN IF EXISTS agent_id;
......@@ -213,9 +213,6 @@ export const players = pgTable(
// Transfer and financial information
onLoan: boolean('on_loan').default(false),
agentId: integer('agent_id').references(() => agents.id, {
onDelete: 'set null',
}),
ranking: varchar('ranking', { length: 255 }),
roi: varchar('roi', { length: 255 }), // Return on Investment
marketValue: decimal('market_value', { precision: 15, scale: 2 }), // Monetary value
......@@ -428,7 +425,9 @@ export const agents = pgTable('agents', {
phone: text('phone').notNull(),
status: text('status').notNull(),
address: text('address').notNull(),
country: text('country').notNull(),
countryWyId: integer('country_wy_id')
.notNull()
.references(() => areas.wyId, { onDelete: 'restrict' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
......@@ -1042,6 +1041,7 @@ export const profileLinks = pgTable(
}),
title: varchar('title', { length: 255 }).notNull(), // e.g., "YouTube", "Instagram", "Portfolio"
url: text('url').notNull(),
icon: varchar('icon', { length: 255 }),
order: integer('order').default(0),
isActive: boolean('is_active').default(true),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
......
......@@ -39,11 +39,14 @@ export class AgentsController {
example1: {
summary: 'Create new agent',
value: {
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith@agents.com',
phone: '+1234567890',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
},
},
},
......@@ -53,14 +56,16 @@ export class AgentsController {
schema: {
example: {
id: 1,
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith@agents.com',
phone: '+1234567890',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
createdAt: '2025-01-15T10:30:00Z',
updatedAt: '2025-01-15T10:30:00Z',
deletedAt: null,
},
},
})
......@@ -92,14 +97,16 @@ export class AgentsController {
schema: {
example: {
id: 1,
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith.new@agents.com',
phone: '+1987654321',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
createdAt: '2025-01-15T10:30:00Z',
updatedAt: '2025-01-15T11:00:00Z',
deletedAt: null,
},
},
})
......@@ -122,14 +129,16 @@ export class AgentsController {
schema: {
example: {
id: 1,
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith@agents.com',
phone: '+1234567890',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
createdAt: '2025-01-15T10:30:00Z',
updatedAt: '2025-01-15T10:30:00Z',
deletedAt: null,
},
},
})
......@@ -149,14 +158,16 @@ export class AgentsController {
example: [
{
id: 1,
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith@agents.com',
phone: '+1234567890',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
createdAt: '2025-01-15T10:30:00Z',
updatedAt: '2025-01-15T10:30:00Z',
deletedAt: null,
},
],
},
......@@ -272,14 +283,16 @@ export class AgentsController {
example: [
{
id: 1,
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith@agents.com',
phone: '+1234567890',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
createdAt: '2025-01-15T10:30:00Z',
updatedAt: '2025-01-15T10:30:00Z',
deletedAt: null,
},
],
},
......@@ -302,14 +315,16 @@ export class AgentsController {
example: [
{
id: 1,
firstName: 'John',
lastName: 'Smith',
name: 'John Smith',
type: 'individual',
email: 'john.smith@agents.com',
phone: '+1234567890',
company: 'Elite Sports Management',
status: 'active',
address: '123 Main St, New York',
countryWyId: 1,
description: 'Elite sports agent',
createdAt: '2025-01-15T10:30:00Z',
updatedAt: '2025-01-15T10:30:00Z',
deletedAt: null,
},
],
},
......
......@@ -27,7 +27,7 @@ export class AgentsService {
phone: data.phone!,
status: data.status!,
address: data.address!,
country: data.country!,
countryWyId: data.countryWyId!,
description: data.description ?? null,
createdAt: now as any,
updatedAt: now as any,
......
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import {
IsEmail,
IsNotEmpty,
IsOptional,
IsString,
IsNumber,
} from 'class-validator';
export class CreateAgentDto {
@ApiProperty({ description: 'Agent name' })
......@@ -31,10 +37,10 @@ export class CreateAgentDto {
@IsNotEmpty()
address: string;
@ApiProperty({ description: 'Country' })
@IsString()
@ApiProperty({ description: 'Country WY ID (Wyscout area ID)' })
@IsNumber()
@IsNotEmpty()
country: string;
countryWyId: number;
@ApiPropertyOptional({ description: 'Description' })
@IsOptional()
......
......@@ -561,6 +561,7 @@ export class PlayerFeaturesService {
],
set: {
notes: data.notes ?? null,
deletedAt: null,
updatedAt: new Date(),
},
})
......
......@@ -53,6 +53,16 @@ export interface Agent {
updatedAt: string;
}
export interface Team {
id: number | null;
wyId: number | null;
gsmId: number | null;
name: string | null;
officialName: string | null;
createdAt: string | null;
updatedAt: string | null;
}
export interface StructuredPlayer {
id: number;
wyId: number;
......@@ -72,13 +82,8 @@ export interface StructuredPlayer {
position: PlayerPosition | null;
otherPositions: PlayerPosition[] | null;
foot: string | null;
currentTeamId: number | null;
currentNationalTeamId: number | null;
currentTeamName: string | null;
currentTeamOfficialName: string | null;
currentNationalTeamName: string | null;
currentNationalTeamOfficialName: string | null;
currentTeam: any | null;
currentTeam: Team | null;
currentNationalTeam: Team | null;
gender: string;
status: string;
jerseyNumber: number | null;
......
......@@ -85,8 +85,8 @@ export class PlayersController {
return this.playersService.upsertByWyId(body as any);
}
@Get('by-id/:id')
@ApiOperation({ summary: 'Get player by database ID' })
@Get(':id')
@ApiOperation({ summary: 'Get player by ID' })
@ApiParam({
name: 'id',
type: Number,
......@@ -104,13 +104,23 @@ export class PlayersController {
return player;
}
@Get(':wyId')
@ApiOperation({ summary: 'Get player by wyId' })
@Get('wy/:wyId')
@ApiOperation({ summary: 'Get player by Wyscout ID (wyId)' })
@ApiParam({
name: 'wyId',
type: Number,
description: 'Wyscout ID (wyId) of the player',
})
@ApiOkResponse({ description: 'Player if found', type: Object })
@ApiNotFoundResponse({ description: 'Player not found' })
async getByWyId(
@Param('wyId', ParseIntPipe) wyId: number,
): Promise<StructuredPlayer | undefined> {
return this.playersService.findByWyId(wyId);
const player = await this.playersService.findByWyId(wyId);
if (!player) {
throw new NotFoundException(`Player with wyId ${wyId} not found`);
}
return player;
}
@Get()
......@@ -286,16 +296,16 @@ export class PlayersController {
return this.playersService.findAll(l, o, name, query);
}
@Patch(':wyId')
@Patch(':id')
@ApiOperation({
summary: 'Update player by wyId',
summary: 'Update player by ID',
description:
'Updates an existing player. Only provided fields will be updated.',
'Updates an existing player by database ID. Only provided fields will be updated.',
})
@ApiParam({
name: 'wyId',
name: 'id',
type: Number,
description: 'Wyscout ID of the player to update',
description: 'Database ID of the player to update',
})
@ApiBody({
description: 'Player update payload. All fields are optional.',
......@@ -338,31 +348,31 @@ export class PlayersController {
},
})
@ApiNotFoundResponse({ description: 'Player not found' })
async updateByWyId(
@Param('wyId', ParseIntPipe) wyId: number,
async updateById(
@Param('id', ParseIntPipe) id: number,
@Body() body: UpdatePlayerDto,
): Promise<Player> {
const result = await this.playersService.updateByWyId(wyId, body);
const result = await this.playersService.updateById(id, body);
if (!result) {
throw new NotFoundException(`Player with wyId ${wyId} not found`);
throw new NotFoundException(`Player with ID ${id} not found`);
}
return result;
}
@Delete(':wyId')
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({ summary: 'Delete player by wyId (soft delete)' })
@ApiOperation({ summary: 'Delete player by ID (soft delete)' })
@ApiParam({
name: 'wyId',
name: 'id',
type: Number,
description: 'Wyscout ID of the player to delete',
description: 'Database ID of the player to delete',
})
@ApiNoContentResponse({ description: 'Player deleted successfully' })
@ApiNotFoundResponse({ description: 'Player not found' })
async deleteByWyId(@Param('wyId', ParseIntPipe) wyId: number): Promise<void> {
const result = await this.playersService.deleteByWyId(wyId);
async deleteById(@Param('id', ParseIntPipe) id: number): Promise<void> {
const result = await this.playersService.deleteById(id);
if (!result) {
throw new NotFoundException(`Player with wyId ${wyId} not found`);
throw new NotFoundException(`Player with ID ${id} not found`);
}
}
}
......@@ -48,6 +48,15 @@ export class CreateLinkDto {
order?: number;
@ApiPropertyOptional({
description: 'Icon identifier or URL for the link',
example: 'youtube',
type: String,
})
@IsOptional()
@IsString()
icon?: string;
@ApiPropertyOptional({
description: 'Whether the link is active',
example: true,
type: Boolean,
......
......@@ -31,6 +31,15 @@ export class UpdateLinkDto {
order?: number;
@ApiPropertyOptional({
description: 'Icon identifier or URL for the link',
example: 'youtube',
type: String,
})
@IsOptional()
@IsString()
icon?: string;
@ApiPropertyOptional({
description: 'Whether the link is active',
example: true,
type: Boolean,
......
......@@ -175,6 +175,7 @@ export class ProfilesService {
coachId: data.coachId ?? null,
title: data.title,
url: data.url,
icon: (data as any).icon ?? null,
order: data.order ?? 0,
isActive: data.isActive ?? true,
} as NewProfileLink)
......@@ -202,6 +203,7 @@ export class ProfilesService {
const updateData: any = { updatedAt: new Date() };
if (data.title !== undefined) updateData.title = data.title;
if (data.url !== undefined) updateData.url = data.url;
if ((data as any).icon !== undefined) updateData.icon = (data as any).icon;
if (data.order !== undefined) updateData.order = data.order;
if (data.isActive !== undefined) updateData.isActive = data.isActive;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment