feat(19-01): update Zod schemas, types, and seed script for reference model
- Add globalItemId and purchasePriceCents to createItemSchema - Add globalItemId to createCandidateSchema - Add tags param to searchGlobalItemsSchema - Remove linkItemSchema from schemas and types - Replace ItemGlobalLink with Tag and GlobalItemTag types - Convert seedGlobalItems to async, add seedTags with 30 curated tags
This commit is contained in:
@@ -1,27 +1,73 @@
|
|||||||
import seedData from "./global-items-seed.json";
|
import seedData from "./global-items-seed.json";
|
||||||
import { db as prodDb } from "./index.ts";
|
import { db as prodDb } from "./index.ts";
|
||||||
import { globalItems } from "./schema.ts";
|
import { globalItems, tags } from "./schema.ts";
|
||||||
|
|
||||||
type Db = typeof prodDb;
|
type Db = typeof prodDb;
|
||||||
|
|
||||||
|
const SEED_TAGS = [
|
||||||
|
"handlebar-bag",
|
||||||
|
"framebag",
|
||||||
|
"saddlebag",
|
||||||
|
"top-tube-bag",
|
||||||
|
"stem-bag",
|
||||||
|
"fork-bag",
|
||||||
|
"hip-pack",
|
||||||
|
"backpack",
|
||||||
|
"tent",
|
||||||
|
"bivy",
|
||||||
|
"tarp",
|
||||||
|
"hammock",
|
||||||
|
"sleeping-bag",
|
||||||
|
"sleeping-pad",
|
||||||
|
"quilt",
|
||||||
|
"pillow",
|
||||||
|
"stove",
|
||||||
|
"cookware",
|
||||||
|
"water-filter",
|
||||||
|
"water-bottle",
|
||||||
|
"headlamp",
|
||||||
|
"bike-light",
|
||||||
|
"ultralight",
|
||||||
|
"waterproof",
|
||||||
|
"budget",
|
||||||
|
"premium",
|
||||||
|
"bikepacking",
|
||||||
|
"hiking",
|
||||||
|
"camping",
|
||||||
|
"touring",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seed curated tags for outdoor/adventure gear.
|
||||||
|
* Idempotent: skips if any tags already exist.
|
||||||
|
*/
|
||||||
|
export async function seedTags(db: Db = prodDb) {
|
||||||
|
const existing = await db.select().from(tags).limit(1);
|
||||||
|
if (existing.length > 0) return;
|
||||||
|
|
||||||
|
for (const name of SEED_TAGS) {
|
||||||
|
await db.insert(tags).values({ name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Seed the global items table with initial bikepacking gear data.
|
* Seed the global items table with initial bikepacking gear data.
|
||||||
* Idempotent: skips if any rows already exist.
|
* Idempotent: skips if any rows already exist.
|
||||||
*/
|
*/
|
||||||
export function seedGlobalItems(db: Db = prodDb) {
|
export async function seedGlobalItems(db: Db = prodDb) {
|
||||||
const existing = db.select().from(globalItems).limit(1).all();
|
const existing = await db.select().from(globalItems).limit(1);
|
||||||
if (existing.length > 0) return;
|
if (existing.length > 0) return;
|
||||||
|
|
||||||
for (const item of seedData) {
|
for (const item of seedData) {
|
||||||
db.insert(globalItems)
|
await db.insert(globalItems).values({
|
||||||
.values({
|
brand: item.brand,
|
||||||
brand: item.brand,
|
model: item.model,
|
||||||
model: item.model,
|
category: item.category ?? null,
|
||||||
category: item.category ?? null,
|
weightGrams: item.weightGrams ?? null,
|
||||||
weightGrams: item.weightGrams ?? null,
|
priceCents: item.priceCents ?? null,
|
||||||
priceCents: item.priceCents ?? null,
|
description: item.description ?? null,
|
||||||
description: item.description ?? null,
|
});
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await seedTags(db);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export const createItemSchema = z.object({
|
|||||||
imageFilename: z.string().optional(),
|
imageFilename: z.string().optional(),
|
||||||
imageSourceUrl: z.string().url().optional().or(z.literal("")),
|
imageSourceUrl: z.string().url().optional().or(z.literal("")),
|
||||||
quantity: z.number().int().positive().optional(),
|
quantity: z.number().int().positive().optional(),
|
||||||
|
globalItemId: z.number().int().positive().optional(),
|
||||||
|
purchasePriceCents: z.number().int().nonnegative().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateItemSchema = createItemSchema.partial().extend({
|
export const updateItemSchema = createItemSchema.partial().extend({
|
||||||
@@ -58,6 +60,7 @@ export const createCandidateSchema = z.object({
|
|||||||
status: candidateStatusSchema.optional(),
|
status: candidateStatusSchema.optional(),
|
||||||
pros: z.string().optional(),
|
pros: z.string().optional(),
|
||||||
cons: z.string().optional(),
|
cons: z.string().optional(),
|
||||||
|
globalItemId: z.number().int().positive().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateCandidateSchema = createCandidateSchema.partial();
|
export const updateCandidateSchema = createCandidateSchema.partial();
|
||||||
@@ -95,10 +98,7 @@ export const updateClassificationSchema = z.object({
|
|||||||
// Global item schemas
|
// Global item schemas
|
||||||
export const searchGlobalItemsSchema = z.object({
|
export const searchGlobalItemsSchema = z.object({
|
||||||
q: z.string().optional(),
|
q: z.string().optional(),
|
||||||
});
|
tags: z.string().optional(),
|
||||||
|
|
||||||
export const linkItemSchema = z.object({
|
|
||||||
globalItemId: z.number().int().positive(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Profile schemas
|
// Profile schemas
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import type { z } from "zod";
|
|||||||
import type {
|
import type {
|
||||||
categories,
|
categories,
|
||||||
globalItems,
|
globalItems,
|
||||||
itemGlobalLinks,
|
globalItemTags,
|
||||||
items,
|
items,
|
||||||
setupItems,
|
setupItems,
|
||||||
setups,
|
setups,
|
||||||
|
tags,
|
||||||
threadCandidates,
|
threadCandidates,
|
||||||
threads,
|
threads,
|
||||||
} from "../db/schema.ts";
|
} from "../db/schema.ts";
|
||||||
@@ -15,7 +16,6 @@ import type {
|
|||||||
createItemSchema,
|
createItemSchema,
|
||||||
createSetupSchema,
|
createSetupSchema,
|
||||||
createThreadSchema,
|
createThreadSchema,
|
||||||
linkItemSchema,
|
|
||||||
reorderCandidatesSchema,
|
reorderCandidatesSchema,
|
||||||
resolveThreadSchema,
|
resolveThreadSchema,
|
||||||
searchGlobalItemsSchema,
|
searchGlobalItemsSchema,
|
||||||
@@ -49,7 +49,6 @@ export type UpdateClassification = z.infer<typeof updateClassificationSchema>;
|
|||||||
|
|
||||||
// Global item types
|
// Global item types
|
||||||
export type SearchGlobalItems = z.infer<typeof searchGlobalItemsSchema>;
|
export type SearchGlobalItems = z.infer<typeof searchGlobalItemsSchema>;
|
||||||
export type LinkItem = z.infer<typeof linkItemSchema>;
|
|
||||||
export type UpdateProfile = z.infer<typeof updateProfileSchema>;
|
export type UpdateProfile = z.infer<typeof updateProfileSchema>;
|
||||||
|
|
||||||
// Types inferred from Drizzle schema
|
// Types inferred from Drizzle schema
|
||||||
@@ -60,4 +59,5 @@ export type ThreadCandidate = typeof threadCandidates.$inferSelect;
|
|||||||
export type Setup = typeof setups.$inferSelect;
|
export type Setup = typeof setups.$inferSelect;
|
||||||
export type SetupItem = typeof setupItems.$inferSelect;
|
export type SetupItem = typeof setupItems.$inferSelect;
|
||||||
export type GlobalItem = typeof globalItems.$inferSelect;
|
export type GlobalItem = typeof globalItems.$inferSelect;
|
||||||
export type ItemGlobalLink = typeof itemGlobalLinks.$inferSelect;
|
export type Tag = typeof tags.$inferSelect;
|
||||||
|
export type GlobalItemTag = typeof globalItemTags.$inferSelect;
|
||||||
|
|||||||
Reference in New Issue
Block a user