- Setup service: LEFT JOIN globalItems in getAllSetups totals and getSetupWithItems - Totals service: LEFT JOIN globalItems in getCategoryTotals and getGlobalTotals - Profile service: LEFT JOIN globalItems in getPublicProfile totals and getPublicSetupWithItems - CSV service: LEFT JOIN globalItems in exportItemsCsv for merged name/weight/price
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import { and, eq, sql } from "drizzle-orm";
|
|
import type { db as prodDb } from "../../db/index.ts";
|
|
import {
|
|
categories,
|
|
globalItems,
|
|
items,
|
|
setupItems,
|
|
setups,
|
|
users,
|
|
} from "../../db/schema.ts";
|
|
import type { UpdateProfile } from "../../shared/types.ts";
|
|
|
|
type Db = typeof prodDb;
|
|
|
|
export async function updateProfile(
|
|
db: Db,
|
|
userId: number,
|
|
data: UpdateProfile,
|
|
) {
|
|
const [existing] = await db.select().from(users).where(eq(users.id, userId));
|
|
if (!existing) return null;
|
|
|
|
// If no fields to update, return existing user
|
|
const hasUpdates = Object.values(data).some((v) => v !== undefined);
|
|
if (!hasUpdates) return existing;
|
|
|
|
const [updated] = await db
|
|
.update(users)
|
|
.set(data)
|
|
.where(eq(users.id, userId))
|
|
.returning();
|
|
|
|
return updated;
|
|
}
|
|
|
|
export async function getPublicProfile(db: Db, userId: number) {
|
|
const [user] = await db
|
|
.select({
|
|
id: users.id,
|
|
displayName: users.displayName,
|
|
avatarUrl: users.avatarUrl,
|
|
bio: users.bio,
|
|
})
|
|
.from(users)
|
|
.where(eq(users.id, userId));
|
|
|
|
if (!user) return null;
|
|
|
|
const publicSetups = await db
|
|
.select({
|
|
id: setups.id,
|
|
name: setups.name,
|
|
createdAt: setups.createdAt,
|
|
itemCount: sql<number>`COALESCE((
|
|
SELECT COUNT(*) FROM setup_items
|
|
WHERE setup_items.setup_id = setups.id
|
|
), 0)`.as("item_count"),
|
|
totalWeight: sql<number>`COALESCE((
|
|
SELECT SUM(
|
|
COALESCE(
|
|
CASE WHEN items.global_item_id IS NOT NULL THEN global_items.weight_grams ELSE NULL END,
|
|
items.weight_grams
|
|
) * items.quantity
|
|
) FROM setup_items
|
|
JOIN items ON items.id = setup_items.item_id
|
|
LEFT JOIN global_items ON global_items.id = items.global_item_id
|
|
WHERE setup_items.setup_id = setups.id
|
|
), 0)`.as("total_weight"),
|
|
totalCost: sql<number>`COALESCE((
|
|
SELECT SUM(
|
|
COALESCE(
|
|
CASE WHEN items.global_item_id IS NOT NULL THEN global_items.price_cents ELSE NULL END,
|
|
items.price_cents
|
|
) * items.quantity
|
|
) FROM setup_items
|
|
JOIN items ON items.id = setup_items.item_id
|
|
LEFT JOIN global_items ON global_items.id = items.global_item_id
|
|
WHERE setup_items.setup_id = setups.id
|
|
), 0)`.as("total_cost"),
|
|
})
|
|
.from(setups)
|
|
.where(and(eq(setups.userId, userId), eq(setups.isPublic, true)));
|
|
|
|
return { ...user, setups: publicSetups };
|
|
}
|
|
|
|
export async function getPublicSetupWithItems(db: Db, setupId: number) {
|
|
const [setup] = await db
|
|
.select()
|
|
.from(setups)
|
|
.where(and(eq(setups.id, setupId), eq(setups.isPublic, true)));
|
|
|
|
if (!setup) return null;
|
|
|
|
const itemList = await db
|
|
.select({
|
|
id: items.id,
|
|
name: sql<string>`COALESCE(
|
|
CASE WHEN ${items.globalItemId} IS NOT NULL
|
|
THEN ${globalItems.brand} || ' ' || ${globalItems.model}
|
|
ELSE ${items.name}
|
|
END,
|
|
${items.name}
|
|
)`.as("name"),
|
|
weightGrams: sql<number | null>`COALESCE(
|
|
CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.weightGrams} ELSE NULL END,
|
|
${items.weightGrams}
|
|
)`.as("weight_grams"),
|
|
priceCents: sql<number | null>`COALESCE(
|
|
CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.priceCents} ELSE NULL END,
|
|
${items.priceCents}
|
|
)`.as("price_cents"),
|
|
quantity: items.quantity,
|
|
categoryId: items.categoryId,
|
|
notes: items.notes,
|
|
productUrl: items.productUrl,
|
|
imageFilename: sql<string | null>`COALESCE(
|
|
${items.imageFilename},
|
|
CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.imageUrl} ELSE NULL END
|
|
)`.as("image_filename"),
|
|
globalItemId: items.globalItemId,
|
|
createdAt: items.createdAt,
|
|
updatedAt: items.updatedAt,
|
|
categoryName: categories.name,
|
|
categoryIcon: categories.icon,
|
|
classification: setupItems.classification,
|
|
})
|
|
.from(setupItems)
|
|
.innerJoin(items, eq(setupItems.itemId, items.id))
|
|
.innerJoin(categories, eq(items.categoryId, categories.id))
|
|
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
|
|
.where(eq(setupItems.setupId, setupId));
|
|
|
|
return { ...setup, items: itemList };
|
|
}
|