71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
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>;
|