pin to homepage and liveblog ticker
This commit is contained in:
1 parent
5a457fa99f
commit
4ccb65ba88
11 files changed
+782
-157
No files matched your search
@@ -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()
|
||||
|
||||
@@ -174,6 +174,9 @@ export class LiveBlog {
|
||||
})
|
||||
status: LiveBlogStatus;
|
||||
|
||||
@Column({ default: false })
|
||||
isPinned: boolean;
|
||||
|
||||
@Column({ nullable: true })
|
||||
strapiId: string;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user