export enum BlogPostStatus { DRAFT = 'draft', SCHEDULED = 'scheduled', PUBLISHED = 'published', ARCHIVED = 'archived', } export type BlogPostCategory = 'industry' | 'technology' | 'guides' | 'news'; interface BlogPostProps { id: string; title: string; slug: string; excerpt: string; content: string; coverImageUrl?: string; category: BlogPostCategory; tags: string[]; authorName: string; status: BlogPostStatus; isFeatured: boolean; publishedAt?: Date; metaTitle?: string; metaDescription?: string; primaryKeyword?: string; secondaryKeywords: string[]; createdAt: Date; updatedAt: Date; } export class BlogPost { private constructor(private readonly props: BlogPostProps) {} static create( props: Omit ): BlogPost { const now = new Date(); return new BlogPost({ ...props, secondaryKeywords: props.secondaryKeywords ?? [], status: BlogPostStatus.DRAFT, isFeatured: false, createdAt: now, updatedAt: now, }); } static fromPersistence(props: BlogPostProps): BlogPost { return new BlogPost(props); } get id(): string { return this.props.id; } get title(): string { return this.props.title; } get slug(): string { return this.props.slug; } get excerpt(): string { return this.props.excerpt; } get content(): string { return this.props.content; } get coverImageUrl(): string | undefined { return this.props.coverImageUrl; } get category(): BlogPostCategory { return this.props.category; } get tags(): string[] { return this.props.tags; } get authorName(): string { return this.props.authorName; } get status(): BlogPostStatus { return this.props.status; } get isFeatured(): boolean { return this.props.isFeatured; } get publishedAt(): Date | undefined { return this.props.publishedAt; } get metaTitle(): string | undefined { return this.props.metaTitle; } get metaDescription(): string | undefined { return this.props.metaDescription; } get primaryKeyword(): string | undefined { return this.props.primaryKeyword; } get secondaryKeywords(): string[] { return this.props.secondaryKeywords; } get createdAt(): Date { return this.props.createdAt; } get updatedAt(): Date { return this.props.updatedAt; } update( data: Partial< Pick< BlogPostProps, | 'title' | 'slug' | 'excerpt' | 'content' | 'coverImageUrl' | 'category' | 'tags' | 'authorName' | 'isFeatured' | 'metaTitle' | 'metaDescription' | 'primaryKeyword' | 'secondaryKeywords' > > ): BlogPost { return new BlogPost({ ...this.props, ...data, updatedAt: new Date() }); } publish(): BlogPost { return new BlogPost({ ...this.props, status: BlogPostStatus.PUBLISHED, publishedAt: this.props.publishedAt ?? new Date(), updatedAt: new Date(), }); } schedule(date: Date): BlogPost { return new BlogPost({ ...this.props, status: BlogPostStatus.SCHEDULED, publishedAt: date, updatedAt: new Date(), }); } archive(): BlogPost { return new BlogPost({ ...this.props, status: BlogPostStatus.ARCHIVED, updatedAt: new Date() }); } unpublish(): BlogPost { return new BlogPost({ ...this.props, status: BlogPostStatus.DRAFT, publishedAt: undefined, updatedAt: new Date(), }); } isPublished(): boolean { return this.props.status === BlogPostStatus.PUBLISHED; } isVisibleToPublic(): boolean { if (this.props.status === BlogPostStatus.PUBLISHED) return true; if ( this.props.status === BlogPostStatus.SCHEDULED && this.props.publishedAt && this.props.publishedAt <= new Date() ) return true; return false; } toObject(): BlogPostProps { return { ...this.props }; } }