pin to homepage and liveblog ticker

This commit is contained in:
dimitar committed 2026-01-29 06:06:54 +01:00
1 parent 5a457fa99f
commit 4ccb65ba88
11 files changed
+782 -157

No files matched your search

+14 -2
View File
@@ -138,6 +138,10 @@ export class CreateLiveBlogDto {
@IsEnum(LiveBlogStatus)
status?: LiveBlogStatus;
@IsOptional()
@IsBoolean()
isPinned?: boolean;
@IsOptional()
@IsString()
strapiId?: string;
@@ -168,6 +172,10 @@ export class UpdateLiveBlogDto {
@IsEnum(LiveBlogStatus)
status?: LiveBlogStatus;
@IsOptional()
@IsBoolean()
isPinned?: boolean;
@IsOptional()
@IsString()
strapiId?: string;
@@ -234,8 +242,12 @@ export class FindLiveBlogsDto {
author?: string;
@IsOptional()
@IsEnum(LiveBlogStatus)
status?: LiveBlogStatus;
@IsString()
status?: string;
@IsOptional()
@IsBoolean()
isPinned?: boolean;
@IsOptional()
@IsString()
+3
View File
@@ -174,6 +174,9 @@ export class LiveBlog {
})
status: LiveBlogStatus;
@Column({ default: false })
isPinned: boolean;
@Column({ nullable: true })
strapiId: string;
+16 -5
View File
@@ -29,6 +29,22 @@ export class LiveBlogController {
constructor(private readonly liveBlogService: LiveBlogService) {}
// Live Blog CRUD operations
@Get('featured')
getFeatured() {
this.logger.log('GET /featured called');
return this.liveBlogService.findPinned();
}
@Get('active')
getActive() {
return this.liveBlogService.findActive();
}
@Get('recent')
getRecent() {
return this.liveBlogService.getLiveBlogsWithRecentUpdates();
}
@Post()
create(
@Body(new ValidationPipe({ transform: true })) dto: CreateLiveBlogDto,
@@ -43,11 +59,6 @@ export class LiveBlogController {
return this.liveBlogService.findAll(dto);
}
@Get('recent')
getRecent() {
return this.liveBlogService.getLiveBlogsWithRecentUpdates();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.liveBlogService.findOne(id);
+86 -64
View File
@@ -5,7 +5,7 @@ import {
OnModuleInit,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Repository, In } from 'typeorm';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Response } from 'express';
import { LiveBlog, LiveBlogUpdate, LiveBlogStatus } from './entities';
@@ -96,7 +96,8 @@ export class LiveBlogService implements OnModuleInit {
const {
category,
author,
status = LiveBlogStatus.LIVE,
status,
isPinned,
search,
page = 1,
limit = 10,
@@ -105,8 +106,23 @@ export class LiveBlogService implements OnModuleInit {
const queryBuilder = this.liveBlogRepository
.createQueryBuilder('liveBlog')
.leftJoinAndSelect('liveBlog.author', 'author')
.leftJoinAndSelect('liveBlog.category', 'category')
.where('liveBlog.status = :status', { status });
.leftJoinAndSelect('liveBlog.category', 'category');
// Handle status filter - can be single value or comma-separated list
if (status) {
if (typeof status === 'string' && status.includes(',')) {
const statuses = status.split(',').map((s) => s.trim());
queryBuilder.where('liveBlog.status IN (:...statuses)', { statuses });
} else {
queryBuilder.where('liveBlog.status = :status', { status });
}
} else if (!isPinned) {
// Default to live blogs if no status specified AND not querying for pinned blogs
// Pinned blogs can be either live or ended
queryBuilder.where('liveBlog.status = :status', {
status: LiveBlogStatus.LIVE,
});
}
if (category) {
queryBuilder.andWhere('category.slug = :category', { category });
@@ -123,6 +139,10 @@ export class LiveBlogService implements OnModuleInit {
);
}
if (isPinned !== undefined) {
queryBuilder.andWhere('liveBlog.isPinned = :isPinned', { isPinned });
}
const [data, total] = await queryBuilder
.orderBy('liveBlog.createdAt', 'DESC')
.skip((page - 1) * limit)
@@ -178,82 +198,63 @@ export class LiveBlogService implements OnModuleInit {
}
async update(id: string, dto: UpdateLiveBlogDto): Promise<LiveBlog> {
// Build SET clause for update
const setClauses: string[] = [];
const params: any[] = [];
const liveBlog = await this.liveBlogRepository.findOne({
where: { id },
relations: ['author', 'category'],
});
if (!liveBlog) {
throw new NotFoundException(`Live blog with ID ${id} not found`);
}
// Track if status changed for event emission
const oldStatus = liveBlog.status;
// Update fields
if (dto.title !== undefined) {
setClauses.push('title = ?');
params.push(dto.title);
liveBlog.title = dto.title;
}
if (dto.slug !== undefined) {
setClauses.push('slug = ?');
params.push(dto.slug);
liveBlog.slug = dto.slug;
}
if (dto.description !== undefined) {
setClauses.push('description = ?');
params.push(dto.description);
liveBlog.description = dto.description;
}
if (dto.status !== undefined) {
setClauses.push('status = ?');
params.push(dto.status);
liveBlog.status = dto.status;
}
if (dto.strapiId !== undefined) {
setClauses.push('strapiId = ?');
params.push(dto.strapiId);
liveBlog.strapiId = dto.strapiId;
}
if (dto.authorId !== undefined) {
setClauses.push('authorId = ?');
params.push(dto.authorId);
liveBlog.authorId = dto.authorId;
}
if (dto.categoryId !== undefined) {
setClauses.push('categoryId = ?');
params.push(dto.categoryId);
liveBlog.categoryId = dto.categoryId;
}
// Always update updatedAt
setClauses.push('updatedAt = CURRENT_TIMESTAMP');
if (setClauses.length === 0) {
// Nothing to update
return this.findOneWithoutIncrement(id);
if (dto.isPinned !== undefined) {
liveBlog.isPinned = dto.isPinned;
}
// Add id to params
params.push(id);
// Execute raw SQL update
const queryRunner = this.liveBlogRepository.manager.connection.createQueryRunner();
await queryRunner.connect();
try {
await queryRunner.query(
`UPDATE live_blogs SET ${setClauses.join(', ')} WHERE id = ?`,
params
);
// Emit status change event if status changed
if (dto.status) {
const currentBlog = await this.findOneWithoutIncrement(id);
if (currentBlog && dto.status !== currentBlog.status) {
this.eventEmitter.emit('live-blog.status-change', {
blogId: id,
status: dto.status,
});
}
}
// Return the updated entity
return this.findOneWithoutIncrement(id);
} finally {
await queryRunner.release();
// Save the updated entity
const updatedBlog = await this.liveBlogRepository.save(liveBlog);
// Emit status change event if status changed
if (dto.status !== undefined && dto.status !== oldStatus) {
this.eventEmitter.emit('live-blog.status-change', {
blogId: id,
status: dto.status,
});
}
return updatedBlog;
}
async remove(id: string): Promise<void> {
@@ -444,4 +445,25 @@ export class LiveBlogService implements OnModuleInit {
.orderBy('updates.createdAt', 'DESC')
.getMany();
}
async findPinned(): Promise<LiveBlog[]> {
return this.liveBlogRepository.find({
where: {
isPinned: true,
status: In([LiveBlogStatus.LIVE, LiveBlogStatus.ENDED]), // Show both live and ended pinned blogs
},
relations: ['author', 'category', 'updates'],
order: { updatedAt: 'DESC' },
take: 5,
});
}
async findActive(): Promise<LiveBlog[]> {
return this.liveBlogRepository.find({
where: { status: LiveBlogStatus.LIVE },
relations: ['author', 'category'],
order: { createdAt: 'DESC' },
take: 20,
});
}
}