Expose portfolio projectUrl and document it for websites.

Wire create/update/list/detail to project_url and sync website OpenAPI, Postman, and AI brief.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-23 16:47:46 +03:30
co-authored by Cursor
parent 37bce1e9fa
commit ad6f9dcf1d
8 changed files with 46 additions and 8 deletions
+10
View File
@@ -159,6 +159,7 @@ export class PortfoliosService {
slug,
description: dto.abstract?.trim() || null,
content: this.buildContent(dto.mainTextHtml) as Prisma.InputJsonValue,
project_url: this.normalizeProjectUrl(dto.projectUrl),
status,
featured_media_id: featuredMediaId,
sort_order: sortOrder,
@@ -300,6 +301,9 @@ export class PortfoliosService {
if (dto.abstract !== undefined) {
data.description = dto.abstract?.trim() || null;
}
if (dto.projectUrl !== undefined) {
data.project_url = this.normalizeProjectUrl(dto.projectUrl);
}
if (dto.status !== undefined) {
data.status = dto.status;
}
@@ -743,6 +747,7 @@ export class PortfoliosService {
titleEn: portfolio.title_en?.trim() || null,
slug: portfolio.slug,
abstract: portfolio.description ?? '',
projectUrl: portfolio.project_url?.trim() || null,
mainTextHtml: (content.html as string | undefined) ?? '',
status: portfolio.status,
categoryId: categoryAssignment?.categoryId.toString() ?? null,
@@ -835,6 +840,11 @@ export class PortfoliosService {
};
}
private normalizeProjectUrl(value?: string | null): string | null {
const trimmed = value?.trim() || '';
return trimmed.length > 0 ? trimmed : null;
}
private async nextSortOrder(businessId: bigint): Promise<number> {
const result = await this.prisma.portfolios.aggregate({
where: { business_id: businessId },