turash/bugulma/frontend/schemas/backend/resource-flow.ts

208 lines
7.3 KiB
TypeScript

import { z } from 'zod';
import {
createBackendEntitySchema,
idSchema,
nonNegativeNumberSchema,
positiveNumberSchema,
} from '@/schemas/common';
/**
* Backend-aligned Resource Flow schemas
* Matches the Go backend domain.ResourceFlow struct and related types
*
* Uses Zod v4's composition and enum features for DRY code
*/
// ============================================================================
// Enum Schemas (using Zod v4's improved enum support)
// ============================================================================
export const resourceDirectionSchema = z
.enum(['input', 'output'])
.describe('Resource flow direction');
export type ResourceDirection = z.infer<typeof resourceDirectionSchema>;
export const resourceTypeSchema = z
.enum([
'heat',
'water',
'steam',
'CO2',
'biowaste',
'cooling',
'logistics',
'materials',
'service',
])
.describe('Type of resource');
export type ResourceType = z.infer<typeof resourceTypeSchema>;
export const precisionLevelSchema = z
.enum(['rough', 'estimated', 'measured'])
.describe('Precision level of measurement');
export type PrecisionLevel = z.infer<typeof precisionLevelSchema>;
export const sourceTypeSchema = z
.enum(['declared', 'device', 'calculated'])
.describe('Source of the data');
export type SourceType = z.infer<typeof sourceTypeSchema>;
export const physicalStateSchema = z
.enum(['solid', 'liquid', 'gas'])
.describe('Physical state of the resource');
export type PhysicalState = z.infer<typeof physicalStateSchema>;
// ============================================================================
// Composite Schemas
// ============================================================================
export const qualitySchema = z
.object({
temperature_celsius: z.number().optional().describe('Temperature in Celsius'),
pressure_bar: positiveNumberSchema.optional().describe('Pressure in bar'),
purity_pct: z.number().min(0).max(100).optional().describe('Purity percentage'),
grade: z.string().optional().describe('Quality grade'),
hazardousness: z.boolean().optional().describe('Whether resource is hazardous'),
composition: z.string().optional().describe('Chemical composition'),
physical_state: physicalStateSchema.optional(),
})
.describe('Quality specifications');
export type Quality = z.infer<typeof qualitySchema>;
export const quantitySchema = z
.object({
amount: positiveNumberSchema.describe('Amount of resource'),
unit: z.string().min(1).describe('Unit of measurement'),
temporal_unit: z.string().optional().describe('Temporal unit (e.g., per month)'),
variability: nonNegativeNumberSchema.optional().describe('Variability in amount'),
})
.describe('Quantity specifications');
export type Quantity = z.infer<typeof quantitySchema>;
export const economicDataSchema = z
.object({
cost_in: nonNegativeNumberSchema.optional().describe('Input cost'),
cost_out: nonNegativeNumberSchema.optional().describe('Output cost'),
waste_disposal_cost: nonNegativeNumberSchema.optional().describe('Waste disposal cost'),
primary_input_cost: nonNegativeNumberSchema.optional().describe('Primary input cost'),
transportation_cost: nonNegativeNumberSchema.optional().describe('Transportation cost'),
cost_sharing_fraction: z
.number()
.min(0)
.max(1)
.optional()
.describe('Cost sharing fraction (0-1)'),
})
.describe('Economic data');
export type EconomicData = z.infer<typeof economicDataSchema>;
export const constraintsSchema = z
.object({
max_distance_km: positiveNumberSchema.optional().describe('Maximum distance in kilometers'),
requires_permit: z.boolean().optional().describe('Whether permit is required'),
min_quality_threshold: z.string().optional().describe('Minimum quality threshold'),
regulatory_compliance: z.boolean().optional().describe('Regulatory compliance status'),
})
.describe('Constraints and requirements');
export type Constraints = z.infer<typeof constraintsSchema>;
export const timeRangeSchema = z
.object({
start: z
.string()
.regex(/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/, {
error: 'Time must be in HH:MM format',
})
.describe('Start time (HH:MM)'),
end: z
.string()
.regex(/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/, {
error: 'Time must be in HH:MM format',
})
.describe('End time (HH:MM)'),
})
.describe('Time range');
export type TimeRange = z.infer<typeof timeRangeSchema>;
export const timeProfileSchema = z
.object({
availability: z
.record(z.string(), timeRangeSchema)
.optional()
.describe('Availability by day of week'),
seasonality: z.array(z.string()).optional().describe('Months when available'),
supply_pattern: z
.enum(['continuous', 'batch', 'seasonal', 'on_demand', 'irregular'])
.optional()
.describe('Supply pattern'),
})
.describe('Time profile');
export type TimeProfile = z.infer<typeof timeProfileSchema>;
export const serviceDetailsSchema = z
.object({
domain: z
.string()
.optional()
.describe('Service domain (maintenance, consulting, transport, inspection)'),
mobility: z.enum(['on_site', 'remote']).optional().describe('Service mobility type'),
capacity_h_per_month: positiveNumberSchema.optional().describe('Capacity in hours per month'),
service_area_km: positiveNumberSchema.optional().describe('Service area radius in kilometers'),
})
.describe('Service details');
export type ServiceDetails = z.infer<typeof serviceDetailsSchema>;
// ============================================================================
// Main Resource Flow Schema
// ============================================================================
/**
* Backend Resource Flow schema
* Uses Zod v4's composition features for DRY code
*/
export const backendResourceFlowSchema = createBackendEntitySchema({
OrganizationID: idSchema.describe('Organization ID'),
SiteID: idSchema.describe('Site ID'),
Direction: resourceDirectionSchema,
Type: resourceTypeSchema,
Quality: qualitySchema,
Quantity: quantitySchema,
TimeProfile: timeProfileSchema.optional(),
EconomicData: economicDataSchema.optional(),
Constraints: constraintsSchema.optional(),
ServiceDetails: serviceDetailsSchema.optional(),
PrecisionLevel: precisionLevelSchema.optional(),
SourceType: sourceTypeSchema.optional(),
DeviceSignature: z.string().optional().describe('Device signature for IoT data'),
});
export type BackendResourceFlow = z.infer<typeof backendResourceFlowSchema>;
/**
* Request schema for creating a resource flow
* Uses Zod v4's composition for consistency
*/
export const createResourceFlowRequestSchema = z.object({
organization_id: idSchema.describe('Organization ID'),
site_id: idSchema.describe('Site ID'),
direction: resourceDirectionSchema,
type: resourceTypeSchema,
quality: qualitySchema.optional(),
quantity: quantitySchema,
time_profile: timeProfileSchema.optional(),
economic_data: economicDataSchema.optional(),
constraints: constraintsSchema.optional(),
service_details: serviceDetailsSchema.optional(),
precision_level: precisionLevelSchema.optional(),
source_type: sourceTypeSchema.optional(),
});
export type CreateResourceFlowRequest = z.infer<typeof createResourceFlowRequestSchema>;