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`);
}
}
}
......@@ -6,6 +6,7 @@ import {
areas,
positions,
agents,
playerAgents,
type NewPlayer,
type Player,
type Position,
......@@ -291,26 +292,6 @@ export class PlayersService {
: null,
otherPositions: null, // Will be populated separately if needed
foot: rawPlayer.foot ?? null,
currentTeamId:
rawPlayer.currentTeamId ?? rawPlayer.current_team_id ?? null,
currentNationalTeamId:
rawPlayer.currentNationalTeamId ??
rawPlayer.current_national_team_id ??
null,
currentTeamName:
rawPlayer.currentTeamName ?? rawPlayer.current_team_name ?? null,
currentTeamOfficialName:
rawPlayer.currentTeamOfficialName ??
rawPlayer.current_team_official_name ??
null,
currentNationalTeamName:
rawPlayer.currentNationalTeamName ??
rawPlayer.current_national_team_name ??
null,
currentNationalTeamOfficialName:
rawPlayer.currentNationalTeamOfficialName ??
rawPlayer.current_national_team_official_name ??
null,
currentTeam: (() => {
// Get stored team name and official name
const storedTeamName =
......@@ -325,12 +306,16 @@ export class PlayersService {
// If we have a relation currentTeam, merge stored values into it
if (rawPlayer.currentTeam) {
return {
...rawPlayer.currentTeam,
id: rawPlayer.currentTeam.id,
wyId: rawPlayer.currentTeam.wyId,
gsmId: rawPlayer.currentTeam.gsmId,
name: storedTeamName ?? rawPlayer.currentTeam.name ?? null,
officialName:
storedTeamOfficialName ??
rawPlayer.currentTeam.officialName ??
null,
createdAt: rawPlayer.currentTeam.createdAt,
updatedAt: rawPlayer.currentTeam.updatedAt,
};
}
......@@ -342,27 +327,42 @@ export class PlayersService {
gsmId: null,
name: storedTeamName,
officialName: storedTeamOfficialName,
shortName: null,
description: null,
type: null,
category: null,
gender: null,
areaWyId: null,
city: null,
coachWyId: null,
competitionWyId: null,
seasonWyId: null,
league: null,
season: null,
status: null,
isActive: null,
apiLastSyncedAt: null,
apiSyncStatus: null,
createdAt: null,
updatedAt: null,
deletedAt: null,
area: null,
coach: null,
};
}
return null;
})(),
currentNationalTeam: (() => {
// Get stored national team name and official name
const storedNationalTeamName =
rawPlayer.currentNationalTeamName ??
rawPlayer.current_national_team_name ??
null;
const storedNationalTeamOfficialName =
rawPlayer.currentNationalTeamOfficialName ??
rawPlayer.current_national_team_official_name ??
null;
const nationalTeamId =
rawPlayer.currentNationalTeamId ??
rawPlayer.current_national_team_id ??
null;
// If we have stored values, create object
if (
storedNationalTeamName ||
storedNationalTeamOfficialName ||
nationalTeamId
) {
return {
id: null,
wyId: nationalTeamId,
gsmId: null,
name: storedNationalTeamName,
officialName: storedNationalTeamOfficialName,
createdAt: null,
updatedAt: null,
};
}
......@@ -804,20 +804,6 @@ export class PlayersService {
email: players.email,
phone: players.phone,
onLoan: players.onLoan,
agentId: players.agentId,
agent: {
id: agents.id,
name: agents.name,
description: agents.description,
type: agents.type,
email: agents.email,
phone: agents.phone,
status: agents.status,
address: agents.address,
country: agents.country,
createdAt: agents.createdAt,
updatedAt: agents.updatedAt,
},
ranking: players.ranking,
roi: players.roi,
marketValue: players.marketValue,
......@@ -855,8 +841,7 @@ export class PlayersService {
})
.from(players)
.leftJoin(areas, eq(players.birthAreaWyId, areas.wyId))
.leftJoin(positions, eq(players.positionId, positions.id))
.leftJoin(agents, eq(players.agentId, agents.id));
.leftJoin(positions, eq(players.positionId, positions.id));
// Add passport area join using a subquery approach
// We'll need to do a second query or use a different approach
......@@ -1089,6 +1074,53 @@ export class PlayersService {
});
}
// Fetch agents for all players from playerAgents junction table
const playerIds = rawData.map((p) => p.id);
let playerAgentsMap: Map<number, any> = new Map();
if (playerIds.length > 0) {
const playerAgentRows = await db
.select({
playerId: playerAgents.playerId,
agentId: playerAgents.agentId,
agentName: agents.name,
agentDescription: agents.description,
agentType: agents.type,
agentEmail: agents.email,
agentPhone: agents.phone,
agentStatus: agents.status,
agentAddress: agents.address,
agentCountryWyId: agents.countryWyId,
agentCreatedAt: agents.createdAt,
agentUpdatedAt: agents.updatedAt,
})
.from(playerAgents)
.leftJoin(agents, eq(playerAgents.agentId, agents.id))
.where(inArray(playerAgents.playerId, playerIds));
for (const row of playerAgentRows) {
if (row.playerId && !playerAgentsMap.has(row.playerId)) {
playerAgentsMap.set(row.playerId, {
agentId: row.agentId,
agent: row.agentId
? {
id: row.agentId,
name: row.agentName || '',
description: row.agentDescription || null,
type: row.agentType || '',
email: row.agentEmail || '',
phone: row.agentPhone || '',
status: row.agentStatus || '',
address: row.agentAddress || '',
country: row.agentCountryWyId?.toString() || '',
createdAt: row.agentCreatedAt?.toISOString() || '',
updatedAt: row.agentUpdatedAt?.toISOString() || '',
}
: null,
});
}
}
}
// Attach reports and other positions to each player
structuredData = structuredData.map((player, index) => {
const rawPlayer = rawData[index];
......@@ -1099,10 +1131,14 @@ export class PlayersService {
.filter((pos) => pos !== undefined)
: null;
const playerAgentData = playerAgentsMap.get(rawPlayer.id);
return {
...player,
reports: playerReportsMap.get(player.wyId) ?? [],
otherPositions: otherPositionsArray,
agentId: playerAgentData?.agentId ?? null,
agent: playerAgentData?.agent ?? null,
};
});
......@@ -1361,7 +1397,6 @@ export class PlayersService {
email: normalizeToNull(data.email),
phone: normalizeToNull(data.phone),
onLoan: data.onLoan ?? false,
agentId: data.agentId ?? null,
ranking: normalizeToNull(data.ranking),
roi: normalizeToNull(data.roi),
marketValue: normalizeToNull(data.marketValue),
......@@ -1464,7 +1499,6 @@ export class PlayersService {
email: players.email,
phone: players.phone,
onLoan: players.onLoan,
agentId: players.agentId,
ranking: players.ranking,
roi: players.roi,
marketValue: players.marketValue,
......@@ -1674,6 +1708,46 @@ export class PlayersService {
rating: report.rating ? parseFloat(report.rating as string) : null,
}));
// Fetch agent from playerAgents junction table
const playerAgentRow = await db
.select({
agentId: playerAgents.agentId,
agentName: agents.name,
agentDescription: agents.description,
agentType: agents.type,
agentEmail: agents.email,
agentPhone: agents.phone,
agentStatus: agents.status,
agentAddress: agents.address,
agentCountryWyId: agents.countryWyId,
agentCreatedAt: agents.createdAt,
agentUpdatedAt: agents.updatedAt,
})
.from(playerAgents)
.leftJoin(agents, eq(playerAgents.agentId, agents.id))
.where(eq(playerAgents.playerId, rawPlayer.id))
.limit(1);
if (playerAgentRow && playerAgentRow.length > 0) {
const row = playerAgentRow[0];
structuredPlayer.agentId = row.agentId;
if (row.agentId && row.agentName) {
structuredPlayer.agent = {
id: row.agentId,
name: row.agentName || '',
description: row.agentDescription || null,
type: row.agentType || '',
email: row.agentEmail || '',
phone: row.agentPhone || '',
status: row.agentStatus || '',
address: row.agentAddress || '',
country: row.agentCountryWyId?.toString() || '',
createdAt: row.agentCreatedAt?.toISOString() || '',
updatedAt: row.agentUpdatedAt?.toISOString() || '',
};
}
}
return structuredPlayer;
}
......@@ -1752,7 +1826,6 @@ export class PlayersService {
email: players.email,
phone: players.phone,
onLoan: players.onLoan,
agentId: players.agentId,
ranking: players.ranking,
roi: players.roi,
marketValue: players.marketValue,
......@@ -1955,6 +2028,46 @@ export class PlayersService {
rating: report.rating ? parseFloat(report.rating as string) : null,
}));
// Fetch agent from playerAgents junction table
const playerAgentRow = await db
.select({
agentId: playerAgents.agentId,
agentName: agents.name,
agentDescription: agents.description,
agentType: agents.type,
agentEmail: agents.email,
agentPhone: agents.phone,
agentStatus: agents.status,
agentAddress: agents.address,
agentCountryWyId: agents.countryWyId,
agentCreatedAt: agents.createdAt,
agentUpdatedAt: agents.updatedAt,
})
.from(playerAgents)
.leftJoin(agents, eq(playerAgents.agentId, agents.id))
.where(eq(playerAgents.playerId, rawPlayer.id))
.limit(1);
if (playerAgentRow && playerAgentRow.length > 0) {
const row = playerAgentRow[0];
structuredPlayer.agentId = row.agentId;
if (row.agentId && row.agentName) {
structuredPlayer.agent = {
id: row.agentId,
name: row.agentName || '',
description: row.agentDescription || null,
type: row.agentType || '',
email: row.agentEmail || '',
phone: row.agentPhone || '',
status: row.agentStatus || '',
address: row.agentAddress || '',
country: row.agentCountryWyId?.toString() || '',
createdAt: row.agentCreatedAt?.toISOString() || '',
updatedAt: row.agentUpdatedAt?.toISOString() || '',
};
}
}
return structuredPlayer;
}
......@@ -2118,7 +2231,6 @@ export class PlayersService {
if (data.phone !== undefined)
updateData.phone = normalizeToNull(data.phone);
if (data.onLoan !== undefined) updateData.onLoan = data.onLoan;
if (data.agentId !== undefined) updateData.agentId = data.agentId;
if (data.ranking !== undefined)
updateData.ranking = normalizeToNull(data.ranking);
if (data.roi !== undefined) updateData.roi = normalizeToNull(data.roi);
......@@ -2145,6 +2257,187 @@ export class PlayersService {
return result as Player;
}
async updateById(
id: number,
data: Partial<NewPlayer | any>,
): Promise<Player | null> {
const db = this.databaseService.getDatabase();
// Check if player exists
const existingPlayer = await db
.select()
.from(players)
.leftJoin(areas, eq(players.birthAreaWyId, areas.wyId))
.leftJoin(positions, eq(players.positionId, positions.id))
.where(eq(players.id, id))
.limit(1);
if (!existingPlayer || existingPlayer.length === 0) {
return null;
}
// Normalize date/timestamp fields that may arrive as strings
const toDate = (value: unknown): Date | undefined => {
if (value == null) return undefined;
if (value instanceof Date) return value;
const d = new Date(value as any);
return isNaN(d.getTime()) ? undefined : d;
};
// Convert to date string (YYYY-MM-DD) for date columns
const toDateString = (value: unknown): string | null => {
if (value == null || value === '') return null;
if (value instanceof Date) {
return value.toISOString().split('T')[0];
}
if (typeof value === 'string') {
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
return value;
}
const d = new Date(value);
if (!isNaN(d.getTime())) {
return d.toISOString().split('T')[0];
}
}
return null;
};
// Normalize empty strings to null for nullable fields
const normalizeToNull = (value: unknown): any => {
if (value === '' || value === null || value === undefined) return null;
return value;
};
// Build update object with only provided fields
const updateData: Partial<NewPlayer> = {
updatedAt: new Date(),
};
// Transform and include only fields that are provided
if (data.firstName !== undefined) updateData.firstName = data.firstName;
if (data.lastName !== undefined) updateData.lastName = data.lastName;
if (data.middleName !== undefined)
updateData.middleName = normalizeToNull(data.middleName);
if (data.shortName !== undefined)
updateData.shortName = normalizeToNull(data.shortName);
if (data.gsmId !== undefined)
updateData.gsmId = normalizeToNull(data.gsmId);
if (data.teamWyId !== undefined)
updateData.teamWyId = normalizeToNull(data.teamWyId);
if (data.currentTeamId !== undefined)
updateData.currentTeamId = normalizeToNull(data.currentTeamId);
if (data.currentNationalTeamId !== undefined)
updateData.currentNationalTeamId = normalizeToNull(
data.currentNationalTeamId,
);
if (data.currentTeamName !== undefined)
updateData.currentTeamName = normalizeToNull(data.currentTeamName);
if (data.currentTeamOfficialName !== undefined)
updateData.currentTeamOfficialName = normalizeToNull(
data.currentTeamOfficialName,
);
if (data.currentNationalTeamName !== undefined)
updateData.currentNationalTeamName = normalizeToNull(
data.currentNationalTeamName,
);
if (data.currentNationalTeamOfficialName !== undefined)
updateData.currentNationalTeamOfficialName = normalizeToNull(
data.currentNationalTeamOfficialName,
);
if (data.dateOfBirth !== undefined)
updateData.dateOfBirth = toDateString(data.dateOfBirth) as any;
if (data.heightCm !== undefined)
updateData.heightCm = normalizeToNull(data.heightCm);
if (data.weightKg !== undefined)
updateData.weightKg = normalizeToNull(data.weightKg);
if (data.foot !== undefined) updateData.foot = normalizeToNull(data.foot);
if (data.gender !== undefined)
updateData.gender = normalizeToNull(data.gender);
if (data.position !== undefined || data.positionId !== undefined) {
updateData.positionId = await this.resolvePositionId(
data.position,
data.positionId,
);
}
if (
data.otherPositions !== undefined ||
data.otherPositionIds !== undefined
) {
updateData.otherPositionIds = await this.resolvePositionIds(
data.otherPositions,
data.otherPositionIds,
);
}
if (data.roleCode2 !== undefined)
updateData.roleCode2 = normalizeToNull(data.roleCode2);
if (data.roleCode3 !== undefined)
updateData.roleCode3 = normalizeToNull(data.roleCode3);
if (data.roleName !== undefined)
updateData.roleName = normalizeToNull(data.roleName);
if (data.birthAreaWyId !== undefined)
updateData.birthAreaWyId = normalizeToNull(data.birthAreaWyId);
if (data.secondBirthAreaWyId !== undefined)
updateData.secondBirthAreaWyId = normalizeToNull(
data.secondBirthAreaWyId,
);
if (data.passportAreaWyId !== undefined)
updateData.passportAreaWyId = normalizeToNull(data.passportAreaWyId);
if (data.secondPassportAreaWyId !== undefined)
updateData.secondPassportAreaWyId = normalizeToNull(
data.secondPassportAreaWyId,
);
if (data.status !== undefined) updateData.status = data.status;
// Support both imageDataUrl (internal) and imageDataURL (external alias)
if (
data.imageDataUrl !== undefined ||
(data as any).imageDataURL !== undefined
) {
const imageValue =
data.imageDataUrl !== undefined
? data.imageDataUrl
: (data as any).imageDataURL;
updateData.imageDataUrl = normalizeToNull(imageValue);
}
if (data.jerseyNumber !== undefined)
updateData.jerseyNumber = normalizeToNull(data.jerseyNumber);
if (data.apiLastSyncedAt !== undefined)
updateData.apiLastSyncedAt = toDate(data.apiLastSyncedAt) as any;
if (data.apiSyncStatus !== undefined)
updateData.apiSyncStatus = data.apiSyncStatus;
if (data.isActive !== undefined) updateData.isActive = data.isActive;
// New fields added for contact and financial information
if (data.email !== undefined)
updateData.email = normalizeToNull(data.email);
if (data.phone !== undefined)
updateData.phone = normalizeToNull(data.phone);
if (data.onLoan !== undefined) updateData.onLoan = data.onLoan;
if (data.ranking !== undefined)
updateData.ranking = normalizeToNull(data.ranking);
if (data.roi !== undefined) updateData.roi = normalizeToNull(data.roi);
if (data.marketValue !== undefined)
updateData.marketValue = normalizeToNull(data.marketValue);
if (data.valueRange !== undefined)
updateData.valueRange = normalizeToNull(data.valueRange);
if (data.transferValue !== undefined)
updateData.transferValue = normalizeToNull(data.transferValue);
if (data.salary !== undefined)
updateData.salary = normalizeToNull(data.salary);
if (data.contractEndsAt !== undefined)
updateData.contractEndsAt = toDateString(data.contractEndsAt) as any;
if (data.feasible !== undefined) updateData.feasible = data.feasible;
if (data.morphology !== undefined)
updateData.morphology = normalizeToNull(data.morphology);
const [result] = await db
.update(players)
.set(updateData)
.where(eq(players.id, id))
.returning();
return result as Player;
}
async deleteByWyId(wyId: number): Promise<Player | null> {
const db = this.databaseService.getDatabase();
......@@ -2167,4 +2460,27 @@ export class PlayersService {
return result as Player;
}
async deleteById(id: number): Promise<Player | null> {
const db = this.databaseService.getDatabase();
// Check if player exists
const existingPlayer = await this.findById(id);
if (!existingPlayer) {
return null;
}
// Soft delete: set isActive to false and deletedAt to current timestamp
const [result] = await db
.update(players)
.set({
isActive: false,
deletedAt: new Date(),
updatedAt: new Date(),
})
.where(eq(players.id, id))
.returning();
return result as Player;
}
}
......@@ -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