feat: all services join manufacturers for global item brand display

This commit is contained in:
2026-04-18 16:24:24 +02:00
parent 2924c2269c
commit a508773809
5 changed files with 26 additions and 13 deletions

View File

@@ -1,6 +1,6 @@
import { eq, sql } from "drizzle-orm"; import { eq, sql } from "drizzle-orm";
import type { db as prodDb } from "../../db/index.ts"; import type { db as prodDb } from "../../db/index.ts";
import { categories, globalItems, items } from "../../db/schema.ts"; import { categories, globalItems, items, manufacturers } from "../../db/schema.ts";
import { getOrCreateUncategorized } from "./category.service.ts"; import { getOrCreateUncategorized } from "./category.service.ts";
type Db = typeof prodDb; type Db = typeof prodDb;
@@ -90,7 +90,7 @@ export async function exportItemsCsv(db: Db, userId: number): Promise<string> {
.select({ .select({
name: sql<string>`COALESCE( name: sql<string>`COALESCE(
CASE WHEN ${items.globalItemId} IS NOT NULL CASE WHEN ${items.globalItemId} IS NOT NULL
THEN ${globalItems.brand} || ' ' || ${globalItems.model} THEN ${manufacturers.name} || ' ' || ${globalItems.model}
ELSE ${items.name} ELSE ${items.name}
END, END,
${items.name} ${items.name}
@@ -111,6 +111,7 @@ export async function exportItemsCsv(db: Db, userId: number): Promise<string> {
.from(items) .from(items)
.innerJoin(categories, eq(items.categoryId, categories.id)) .innerJoin(categories, eq(items.categoryId, categories.id))
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id)) .leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
.leftJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(eq(items.userId, userId)); .where(eq(items.userId, userId));
const header = const header =

View File

@@ -4,6 +4,7 @@ import {
globalItems, globalItems,
globalItemTags, globalItemTags,
items, items,
manufacturers,
setupItems, setupItems,
setups, setups,
tags, tags,
@@ -86,14 +87,15 @@ export async function getRecentGlobalItems(
db: Db = prodDb, db: Db = prodDb,
limit = 8, limit = 8,
cursor?: string, cursor?: string,
): Promise<CursorPage<typeof globalItems.$inferSelect>> { ): Promise<CursorPage<typeof globalItems.$inferSelect & { brand: string }>> {
const conditions = cursor const conditions = cursor
? [lt(globalItems.createdAt, new Date(cursor))] ? [lt(globalItems.createdAt, new Date(cursor))]
: []; : [];
const rows = await db const rows = await db
.select() .select({ ...globalItems, brand: manufacturers.name })
.from(globalItems) .from(globalItems)
.innerJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(conditions.length ? and(...conditions) : undefined) .where(conditions.length ? and(...conditions) : undefined)
.orderBy(desc(globalItems.createdAt)) .orderBy(desc(globalItems.createdAt))
.limit(limit + 1); .limit(limit + 1);
@@ -160,7 +162,7 @@ export async function getPopularItemsByTags(
const rows = await db const rows = await db
.select({ .select({
id: globalItems.id, id: globalItems.id,
brand: globalItems.brand, brand: manufacturers.name,
model: globalItems.model, model: globalItems.model,
category: globalItems.category, category: globalItems.category,
weightGrams: globalItems.weightGrams, weightGrams: globalItems.weightGrams,
@@ -170,6 +172,7 @@ export async function getPopularItemsByTags(
ownerCount: sql<number>`CAST(COUNT(DISTINCT ${items.id}) AS INT)`, ownerCount: sql<number>`CAST(COUNT(DISTINCT ${items.id}) AS INT)`,
}) })
.from(globalItems) .from(globalItems)
.innerJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.innerJoin(globalItemTags, eq(globalItemTags.globalItemId, globalItems.id)) .innerJoin(globalItemTags, eq(globalItemTags.globalItemId, globalItems.id))
.innerJoin(tags, eq(tags.id, globalItemTags.tagId)) .innerJoin(tags, eq(tags.id, globalItemTags.tagId))
.leftJoin(items, eq(items.globalItemId, globalItems.id)) .leftJoin(items, eq(items.globalItemId, globalItems.id))

View File

@@ -4,6 +4,7 @@ import {
categories, categories,
globalItems, globalItems,
items, items,
manufacturers,
setupItems, setupItems,
setups, setups,
users, users,
@@ -97,7 +98,7 @@ export async function getPublicSetupWithItems(db: Db, setupId: number) {
id: items.id, id: items.id,
name: sql<string>`COALESCE( name: sql<string>`COALESCE(
CASE WHEN ${items.globalItemId} IS NOT NULL CASE WHEN ${items.globalItemId} IS NOT NULL
THEN ${globalItems.brand} || ' ' || ${globalItems.model} THEN ${manufacturers.name} || ' ' || ${globalItems.model}
ELSE ${items.name} ELSE ${items.name}
END, END,
${items.name} ${items.name}
@@ -129,6 +130,7 @@ export async function getPublicSetupWithItems(db: Db, setupId: number) {
.innerJoin(items, eq(setupItems.itemId, items.id)) .innerJoin(items, eq(setupItems.itemId, items.id))
.innerJoin(categories, eq(items.categoryId, categories.id)) .innerJoin(categories, eq(items.categoryId, categories.id))
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id)) .leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
.leftJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(eq(setupItems.setupId, setupId)); .where(eq(setupItems.setupId, setupId));
return { ...setup, items: itemList }; return { ...setup, items: itemList };

View File

@@ -4,6 +4,7 @@ import {
categories, categories,
globalItems, globalItems,
items, items,
manufacturers,
setupItems, setupItems,
setups, setups,
} from "../../db/schema.ts"; } from "../../db/schema.ts";
@@ -79,7 +80,7 @@ export async function getSetupWithItems(
id: items.id, id: items.id,
name: sql<string>`COALESCE( name: sql<string>`COALESCE(
CASE WHEN ${items.globalItemId} IS NOT NULL CASE WHEN ${items.globalItemId} IS NOT NULL
THEN ${globalItems.brand} || ' ' || ${globalItems.model} THEN ${manufacturers.name} || ' ' || ${globalItems.model}
ELSE ${items.name} ELSE ${items.name}
END, END,
${items.name} ${items.name}
@@ -113,6 +114,7 @@ export async function getSetupWithItems(
.innerJoin(items, eq(setupItems.itemId, items.id)) .innerJoin(items, eq(setupItems.itemId, items.id))
.innerJoin(categories, eq(items.categoryId, categories.id)) .innerJoin(categories, eq(items.categoryId, categories.id))
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id)) .leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
.leftJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(eq(setupItems.setupId, setupId)); .where(eq(setupItems.setupId, setupId));
return { ...setup, items: itemList }; return { ...setup, items: itemList };
@@ -131,7 +133,7 @@ export async function getSetupWithItemsById(db: Db, setupId: number) {
id: items.id, id: items.id,
name: sql<string>`COALESCE( name: sql<string>`COALESCE(
CASE WHEN ${items.globalItemId} IS NOT NULL CASE WHEN ${items.globalItemId} IS NOT NULL
THEN ${globalItems.brand} || ' ' || ${globalItems.model} THEN ${manufacturers.name} || ' ' || ${globalItems.model}
ELSE ${items.name} ELSE ${items.name}
END, END,
${items.name} ${items.name}
@@ -165,6 +167,7 @@ export async function getSetupWithItemsById(db: Db, setupId: number) {
.innerJoin(items, eq(setupItems.itemId, items.id)) .innerJoin(items, eq(setupItems.itemId, items.id))
.innerJoin(categories, eq(items.categoryId, categories.id)) .innerJoin(categories, eq(items.categoryId, categories.id))
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id)) .leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
.leftJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(eq(setupItems.setupId, setupId)); .where(eq(setupItems.setupId, setupId));
return { ...setup, items: itemList }; return { ...setup, items: itemList };
@@ -185,14 +188,14 @@ export async function getSetupItemById(
id: items.id, id: items.id,
name: sql<string>`COALESCE( name: sql<string>`COALESCE(
CASE WHEN ${items.globalItemId} IS NOT NULL CASE WHEN ${items.globalItemId} IS NOT NULL
THEN ${globalItems.brand} || ' ' || ${globalItems.model} THEN ${manufacturers.name} || ' ' || ${globalItems.model}
ELSE ${items.name} ELSE ${items.name}
END, END,
${items.name} ${items.name}
)`.as("name"), )`.as("name"),
brand: sql< brand: sql<
string | null string | null
>`CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.brand} ELSE ${items.brand} END`.as( >`CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${manufacturers.name} ELSE ${items.brand} END`.as(
"brand", "brand",
), ),
weightGrams: sql<number | null>`COALESCE( weightGrams: sql<number | null>`COALESCE(
@@ -221,6 +224,7 @@ export async function getSetupItemById(
.innerJoin(items, eq(setupItems.itemId, items.id)) .innerJoin(items, eq(setupItems.itemId, items.id))
.innerJoin(categories, eq(items.categoryId, categories.id)) .innerJoin(categories, eq(items.categoryId, categories.id))
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id)) .leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
.leftJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(and(eq(setupItems.setupId, setupId), eq(items.id, itemId))); .where(and(eq(setupItems.setupId, setupId), eq(items.id, itemId)));
return row ?? null; return row ?? null;

View File

@@ -4,6 +4,7 @@ import {
categories, categories,
globalItems, globalItems,
items, items,
manufacturers,
threadCandidates, threadCandidates,
threads, threads,
} from "../../db/schema.ts"; } from "../../db/schema.ts";
@@ -82,7 +83,7 @@ export async function getThreadWithCandidates(
threadId: threadCandidates.threadId, threadId: threadCandidates.threadId,
name: sql<string>`COALESCE( name: sql<string>`COALESCE(
CASE WHEN ${threadCandidates.globalItemId} IS NOT NULL CASE WHEN ${threadCandidates.globalItemId} IS NOT NULL
THEN ${globalItems.brand} || ' ' || ${globalItems.model} THEN ${manufacturers.name} || ' ' || ${globalItems.model}
ELSE ${threadCandidates.name} ELSE ${threadCandidates.name}
END, END,
${threadCandidates.name} ${threadCandidates.name}
@@ -118,6 +119,7 @@ export async function getThreadWithCandidates(
.from(threadCandidates) .from(threadCandidates)
.innerJoin(categories, eq(threadCandidates.categoryId, categories.id)) .innerJoin(categories, eq(threadCandidates.categoryId, categories.id))
.leftJoin(globalItems, eq(threadCandidates.globalItemId, globalItems.id)) .leftJoin(globalItems, eq(threadCandidates.globalItemId, globalItems.id))
.leftJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(eq(threadCandidates.threadId, threadId)) .where(eq(threadCandidates.threadId, threadId))
.orderBy(asc(threadCandidates.sortOrder)); .orderBy(asc(threadCandidates.sortOrder));
@@ -367,10 +369,11 @@ export async function resolveThread(
if (candidate.globalItemId) { if (candidate.globalItemId) {
// Reference item — link to global, personal fields only // Reference item — link to global, personal fields only
const [gi] = await tx const [gi] = await tx
.select() .select({ name: manufacturers.name, model: globalItems.model })
.from(globalItems) .from(globalItems)
.innerJoin(manufacturers, eq(globalItems.manufacturerId, manufacturers.id))
.where(eq(globalItems.id, candidate.globalItemId)); .where(eq(globalItems.id, candidate.globalItemId));
const fallbackName = gi ? `${gi.brand} ${gi.model}` : candidate.name; const fallbackName = gi ? `${gi.name} ${gi.model}` : candidate.name;
insertValues = { insertValues = {
name: fallbackName, name: fallbackName,
globalItemId: candidate.globalItemId, globalItemId: candidate.globalItemId,