lomavuokraus/lib/listings.ts
Tero Halla-aho 0bb709d9c5
Some checks failed
CI / checks (push) Has been cancelled
chore: fix audit alerts and formatting
2026-02-04 12:43:03 +02:00

140 lines
3.2 KiB
TypeScript

import { Prisma, ListingStatus } from "@prisma/client";
import { prisma } from "./prisma";
import { DEFAULT_LOCALE, SAMPLE_LISTING_SLUG } from "./sampleListing";
export type ListingWithTranslations = Prisma.ListingTranslationGetPayload<{
include: {
listing: {
include: {
images: {
select: {
id: true;
url: true;
altText: true;
order: true;
isCover: true;
size: true;
mimeType: true;
};
};
owner: true;
};
};
};
}>;
type FetchOptions = {
slug: string;
locale?: string;
includeOwnerDraftsForUserId?: string;
};
function resolveImageUrl(img: {
id: string;
url: string | null;
size: number | null;
}) {
if (img.size && img.size > 0) {
return `/api/images/${img.id}`;
}
return img.url;
}
/**
* Fetch a listing translation by slug and locale.
* Falls back to any locale if the requested locale is missing.
*/
export async function getListingBySlug({
slug,
locale,
includeOwnerDraftsForUserId,
}: FetchOptions): Promise<ListingWithTranslations | null> {
const targetLocale = locale ?? DEFAULT_LOCALE;
const listingWhere: Prisma.ListingWhereInput = includeOwnerDraftsForUserId
? {
removedAt: null,
OR: [
{ status: ListingStatus.PUBLISHED },
{
ownerId: includeOwnerDraftsForUserId,
status: { in: [ListingStatus.DRAFT, ListingStatus.PENDING] },
},
],
}
: { status: ListingStatus.PUBLISHED, removedAt: null };
const translation = await prisma.listingTranslation.findFirst({
where: { slug, locale: targetLocale, listing: listingWhere },
include: {
listing: {
include: {
images: {
orderBy: { order: "asc" },
select: {
id: true,
url: true,
altText: true,
order: true,
isCover: true,
size: true,
mimeType: true,
},
},
owner: true,
},
},
},
});
if (translation) {
return translation;
}
// Fallback: first translation for this slug
return prisma.listingTranslation.findFirst({
where: { slug, listing: listingWhere },
include: {
listing: {
include: {
images: {
orderBy: { order: "asc" },
select: {
id: true,
url: true,
altText: true,
order: true,
isCover: true,
size: true,
mimeType: true,
},
},
owner: true,
},
},
},
orderBy: { createdAt: "asc" },
});
}
export function withResolvedListingImages(
translation: ListingWithTranslations,
): ListingWithTranslations {
const images = translation.listing.images
.map((img) => {
const url = resolveImageUrl(img);
if (!url) return null;
return { ...img, url };
})
.filter(Boolean) as ListingWithTranslations["listing"]["images"];
return {
...translation,
listing: {
...translation.listing,
images,
},
};
}
export { SAMPLE_LISTING_SLUG, DEFAULT_LOCALE };