Files
Cibello-app/packages/validation/src/scans.ts
T
2026-08-05 19:21:11 +07:00

71 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { z } from "zod";
import { SCAN_TYPES } from "@app/shared-types";
import {
confidenceSchema,
dateStringSchema,
quantitySchema,
unitSchema,
uuidSchema,
} from "./common.js";
/** Steg 1: begär signerad uppladdning + skapa jobb (spec §50). */
export const createScanInputSchema = z.object({
scanType: z.enum(SCAN_TYPES),
imageCount: z.number().int().min(0).max(6).default(1),
contentType: z
.enum(["image/jpeg", "image/png", "image/webp", "image/heic"])
.default("image/jpeg"),
/** För streckkod behövs ingen bild koden skickas direkt. */
barcode: z
.string()
.regex(/^\d{8,14}$/)
.optional(),
/** Kontext som förbättrar analysen, t.ex. recept vid tallriksfoto (spec §22). */
context: z
.object({
recipeId: uuidSchema.optional(),
storageLocationId: uuidSchema.optional(),
note: z.string().max(300).optional(),
})
.optional(),
});
export type CreateScanInput = z.infer<typeof createScanInputSchema>;
/** Ett AI-identifierat objekt som användaren granskar (spec §10). */
export const scanResultItemSchema = z.object({
tempId: z.string(),
detectedName: z.string(),
canonicalIngredientId: z.string().nullable(),
brand: z.string().nullable().optional(),
estimatedQuantity: z.number().nullable(),
unit: unitSchema.nullable(),
bestBeforeDate: dateStringSchema.nullable().optional(),
confidence: confidenceSchema,
requiresConfirmation: z.boolean(),
});
export type ScanResultItem = z.infer<typeof scanResultItemSchema>;
/** Steg 3: användaren bekräftar/ändrar innan något skrivs till lagret (spec §10, §61.5). */
export const confirmScanInputSchema = z.object({
storageLocationId: uuidSchema.optional(),
items: z
.array(
z.object({
tempId: z.string().optional(),
action: z.enum(["accept", "edit", "reject", "add"]),
canonicalIngredientId: z.string().max(80).optional(),
displayName: z.string().min(1).max(120),
brand: z.string().max(80).optional(),
quantity: quantitySchema,
unit: unitSchema,
bestBeforeDate: dateStringSchema.optional(),
useByDate: dateStringSchema.optional(),
storageLocationId: uuidSchema.optional(),
sublocation: z.string().max(60).optional(),
priceMinor: z.number().int().min(0).optional(),
}),
)
.max(100),
});
export type ConfirmScanInput = z.infer<typeof confirmScanInputSchema>;