import { apiRequest } from '../lib/api' import { getActiveBusinessId } from '../lib/businessContext' import { extractFirstImageUrl } from '../utils/htmlImages' import type { Portfolio, PortfolioDetail, PortfolioGalleryItem, PortfolioStatus } from '../types/portfolio' export const PORTFOLIOS_PER_PAGE = 12 export interface PortfolioApi { id: string businessId: string title: string titleFa: string titleEn: string | null slug: string abstract: string projectUrl: string | null mainTextHtml: string status: PortfolioStatus categoryId: string | null categoryName: string tags: string[] titleImageUrl: string | null featuredMediaId: string | null gallery: PortfolioGalleryItem[] galleryMediaIds: string[] sortOrder: number commentCount: number publishedAt: string | null createdAt: string updatedAt: string } export interface PortfoliosListResponse { items: PortfolioApi[] total: number page: number pageSize: number } export interface ListPortfoliosParams { page?: number pageSize?: number status?: PortfolioStatus categoryId?: string title?: string } function businessPath(suffix = '') { const businessId = getActiveBusinessId() if (!businessId) { throw new Error('No active business selected. Please sign in again.') } return `/businesses/${businessId}/portfolios${suffix}` } export interface CreatePortfolioPayload { titleFa: string titleEn?: string /** @deprecated Prefer titleFa */ title?: string abstract?: string projectUrl?: string mainTextHtml?: string categoryId?: string status?: PortfolioStatus featuredMediaId?: string galleryMediaIds?: string[] tags?: string[] sortOrder?: number } export interface UpdatePortfolioPayload { titleFa?: string titleEn?: string | null /** @deprecated Prefer titleFa */ title?: string abstract?: string | null projectUrl?: string | null mainTextHtml?: string | null categoryId?: string | null status?: PortfolioStatus featuredMediaId?: string | null galleryMediaIds?: string[] tags?: string[] sortOrder?: number /** Pin to absolute top of the business portfolio list. */ moveToTop?: boolean } export function resolvePortfolioTitleImageUrl(portfolio: PortfolioApi): string | null { const featured = portfolio.titleImageUrl?.trim() if (featured) return featured const galleryUrl = portfolio.gallery?.map((item) => item.url?.trim()).find(Boolean) if (galleryUrl) return galleryUrl return extractFirstImageUrl(portfolio.mainTextHtml) || null } export function resolvePortfolioExternalUrl(url: string | null | undefined): string | null { const trimmed = url?.trim() if (!trimmed) return null if (/^https?:\/\//i.test(trimmed)) return trimmed return `https://${trimmed}` } export function mapPortfolioApiToUi(portfolio: PortfolioApi): Portfolio { return { id: portfolio.id, title: portfolio.titleFa || portfolio.title, titleFa: portfolio.titleFa || portfolio.title, titleEn: portfolio.titleEn ?? null, slug: portfolio.slug, abstract: portfolio.abstract, projectUrl: portfolio.projectUrl?.trim() || null, status: portfolio.status, categoryId: portfolio.categoryId, categoryName: portfolio.categoryName, tags: portfolio.tags, titleImageUrl: resolvePortfolioTitleImageUrl(portfolio), sortOrder: portfolio.sortOrder ?? 0, commentCount: portfolio.commentCount, publishedAt: portfolio.publishedAt, createdAt: portfolio.createdAt, updatedAt: portfolio.updatedAt, } } export function formatPortfolioCardDate( value: string | null, locale: 'en' | 'fa' = 'en', ): string { if (!value) return locale === 'fa' ? 'منتشر نشده' : 'Not published' const date = new Date(value) if (Number.isNaN(date.getTime())) return value return date.toLocaleDateString(locale === 'fa' ? 'fa-IR' : 'en-US', { year: 'numeric', month: 'short', day: 'numeric', }) } export function formatPortfolioDate( value: string | null, locale: 'en' | 'fa' = 'en', ): string { if (!value) return '—' const date = new Date(value) if (Number.isNaN(date.getTime())) return value return date.toLocaleDateString(locale === 'fa' ? 'fa-IR' : 'en-US', { year: 'numeric', month: 'long', day: 'numeric', }) } export function mapPortfolioDetailFromApi(portfolio: PortfolioApi): PortfolioDetail { return { ...mapPortfolioApiToUi(portfolio), mainTextHtml: portfolio.mainTextHtml, gallery: portfolio.gallery ?? [], } } export function mapPortfolioApiToFormState(portfolio: PortfolioApi) { return { titleFa: portfolio.titleFa || portfolio.title, titleEn: portfolio.titleEn ?? '', abstract: portfolio.abstract, mainTextHtml: portfolio.mainTextHtml, projectUrl: portfolio.projectUrl?.trim() || '', categoryId: portfolio.categoryId ?? '', tags: portfolio.tags, titleImage: resolvePortfolioTitleImageUrl(portfolio), featuredMediaId: portfolio.featuredMediaId, images: portfolio.gallery.map((item) => item.url), galleryMediaIds: portfolio.galleryMediaIds, } } export async function listPortfolios( page = 1, pageSize = PORTFOLIOS_PER_PAGE, signal?: AbortSignal, filters?: Omit, ) { const q = new URLSearchParams() q.set('page', String(page)) q.set('pageSize', String(pageSize)) if (filters?.status) q.set('status', filters.status) if (filters?.categoryId) q.set('categoryId', filters.categoryId) if (filters?.title) q.set('title', filters.title) const data = await apiRequest(`${businessPath()}?${q.toString()}`, { auth: true, signal, }) return { ...data, items: data.items.map(mapPortfolioApiToUi), } } export async function getPortfolio(portfolioId: string, signal?: AbortSignal) { const data = await apiRequest<{ portfolio: PortfolioApi }>(businessPath(`/${portfolioId}`), { auth: true, signal, }) return data.portfolio } export async function getPortfolioDetail(portfolioId: string, signal?: AbortSignal) { const portfolio = await getPortfolio(portfolioId, signal) return mapPortfolioDetailFromApi(portfolio) } export async function createPortfolio(payload: CreatePortfolioPayload) { return apiRequest<{ message: string; portfolio: PortfolioApi }>(businessPath(), { method: 'POST', auth: true, body: payload, }) } export async function updatePortfolio(portfolioId: string, payload: UpdatePortfolioPayload) { return apiRequest<{ message: string; portfolio: PortfolioApi }>( businessPath(`/${portfolioId}`), { method: 'PATCH', auth: true, body: payload, }, ) } export async function deletePortfolio(portfolioId: string) { return apiRequest<{ message: string }>(businessPath(`/${portfolioId}`), { method: 'DELETE', auth: true, }) }