171 lines
5.0 KiB
TypeScript
171 lines
5.0 KiB
TypeScript
/**
|
|
* Domain Entity: LiveryTemplate
|
|
*
|
|
* Represents an admin-defined livery template for a specific car.
|
|
* Contains base image and sponsor decal placements.
|
|
*/
|
|
|
|
import type { IEntity } from '@core/shared/domain';
|
|
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
|
import type { LiveryDecal } from '../value-objects/LiveryDecal';
|
|
import { LiveryTemplateId } from './LiveryTemplateId';
|
|
import { LeagueId } from './LeagueId';
|
|
import { SeasonId } from './season/SeasonId';
|
|
import { CarId } from '../value-objects/CarId';
|
|
import { ImageUrl } from './ImageUrl';
|
|
import { LiveryTemplateCreatedAt } from './LiveryTemplateCreatedAt';
|
|
import { LiveryTemplateUpdatedAt } from './LiveryTemplateUpdatedAt';
|
|
|
|
export class LiveryTemplate implements IEntity<LiveryTemplateId> {
|
|
readonly id: LiveryTemplateId;
|
|
readonly leagueId: LeagueId;
|
|
readonly seasonId: SeasonId;
|
|
readonly carId: CarId;
|
|
readonly baseImageUrl: ImageUrl;
|
|
readonly adminDecals: LiveryDecal[];
|
|
readonly createdAt: LiveryTemplateCreatedAt;
|
|
readonly updatedAt: LiveryTemplateUpdatedAt | undefined;
|
|
|
|
private constructor(props: {
|
|
id: LiveryTemplateId;
|
|
leagueId: LeagueId;
|
|
seasonId: SeasonId;
|
|
carId: CarId;
|
|
baseImageUrl: ImageUrl;
|
|
adminDecals: LiveryDecal[];
|
|
createdAt: LiveryTemplateCreatedAt;
|
|
updatedAt?: LiveryTemplateUpdatedAt;
|
|
}) {
|
|
this.id = props.id;
|
|
this.leagueId = props.leagueId;
|
|
this.seasonId = props.seasonId;
|
|
this.carId = props.carId;
|
|
this.baseImageUrl = props.baseImageUrl;
|
|
this.adminDecals = props.adminDecals;
|
|
this.createdAt = props.createdAt;
|
|
this.updatedAt = props.updatedAt;
|
|
}
|
|
|
|
static create(props: {
|
|
id: string;
|
|
leagueId: string;
|
|
seasonId: string;
|
|
carId: string;
|
|
baseImageUrl: string;
|
|
createdAt?: Date;
|
|
adminDecals?: LiveryDecal[];
|
|
}): LiveryTemplate {
|
|
this.validate(props);
|
|
|
|
const id = LiveryTemplateId.create(props.id);
|
|
const leagueId = LeagueId.create(props.leagueId);
|
|
const seasonId = SeasonId.create(props.seasonId);
|
|
const carId = CarId.create(props.carId);
|
|
const baseImageUrl = ImageUrl.create(props.baseImageUrl);
|
|
const createdAt = LiveryTemplateCreatedAt.create(props.createdAt ?? new Date());
|
|
|
|
return new LiveryTemplate({
|
|
id,
|
|
leagueId,
|
|
seasonId,
|
|
carId,
|
|
baseImageUrl,
|
|
adminDecals: props.adminDecals ?? [],
|
|
createdAt,
|
|
});
|
|
}
|
|
|
|
private static validate(props: {
|
|
id: string;
|
|
leagueId: string;
|
|
seasonId: string;
|
|
carId: string;
|
|
baseImageUrl: string;
|
|
}): void {
|
|
if (!props.id || props.id.trim().length === 0) {
|
|
throw new RacingDomainValidationError('LiveryTemplate ID is required');
|
|
}
|
|
|
|
if (!props.leagueId || props.leagueId.trim().length === 0) {
|
|
throw new RacingDomainValidationError('LiveryTemplate leagueId is required');
|
|
}
|
|
|
|
if (!props.seasonId || props.seasonId.trim().length === 0) {
|
|
throw new RacingDomainValidationError('LiveryTemplate seasonId is required');
|
|
}
|
|
|
|
if (!props.carId || props.carId.trim().length === 0) {
|
|
throw new RacingDomainValidationError('LiveryTemplate carId is required');
|
|
}
|
|
|
|
if (!props.baseImageUrl || props.baseImageUrl.trim().length === 0) {
|
|
throw new RacingDomainValidationError('LiveryTemplate baseImageUrl is required');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a decal to the template
|
|
*/
|
|
addDecal(decal: LiveryDecal): LiveryTemplate {
|
|
if (decal.type !== 'sponsor') {
|
|
throw new RacingDomainInvariantError('Only sponsor decals can be added to admin template');
|
|
}
|
|
|
|
return new LiveryTemplate({
|
|
...this,
|
|
adminDecals: [...this.adminDecals, decal],
|
|
updatedAt: LiveryTemplateUpdatedAt.create(new Date()),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove a decal from the template
|
|
*/
|
|
removeDecal(decalId: string): LiveryTemplate {
|
|
const updatedDecals = this.adminDecals.filter(d => d.id !== decalId);
|
|
|
|
if (updatedDecals.length === this.adminDecals.length) {
|
|
throw new RacingDomainValidationError('Decal not found in template');
|
|
}
|
|
|
|
return new LiveryTemplate({
|
|
...this,
|
|
adminDecals: updatedDecals,
|
|
updatedAt: LiveryTemplateUpdatedAt.create(new Date()),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update a decal position
|
|
*/
|
|
updateDecal(decalId: string, updatedDecal: LiveryDecal): LiveryTemplate {
|
|
const index = this.adminDecals.findIndex(d => d.id === decalId);
|
|
|
|
if (index === -1) {
|
|
throw new RacingDomainValidationError('Decal not found in template');
|
|
}
|
|
|
|
const updatedDecals = [...this.adminDecals];
|
|
updatedDecals[index] = updatedDecal;
|
|
|
|
return new LiveryTemplate({
|
|
...this,
|
|
adminDecals: updatedDecals,
|
|
updatedAt: LiveryTemplateUpdatedAt.create(new Date()),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get all sponsor decals
|
|
*/
|
|
getSponsorDecals(): LiveryDecal[] {
|
|
return this.adminDecals.filter(d => d.type === 'sponsor');
|
|
}
|
|
|
|
/**
|
|
* Check if template has sponsor decals
|
|
*/
|
|
hasSponsorDecals(): boolean {
|
|
return this.adminDecals.some(d => d.type === 'sponsor');
|
|
}
|
|
} |