import type { OrganizationFormData } from '@/types'; import type { CreateResourceFlowRequest } from '@/schemas/backend/resource-flow'; import { resourceTypeSchema } from '@/schemas/backend/resource-flow'; /** * Maps user-friendly "needs" and "offers" to backend ResourceFlow requests * This is the translation layer between business-friendly UI and technical backend */ /** * Infer resource type from resource name and category * This is a simple heuristic - in production, you might want more sophisticated mapping */ function inferResourceType(resourceName: string, category?: string): string { const name = resourceName.toLowerCase(); const cat = category?.toLowerCase() || ''; // Map common resource names to backend resource types if (name.includes('heat') || name.includes('тепло') || cat.includes('energy')) { return 'heat'; } if (name.includes('water') || name.includes('вода') || cat.includes('water')) { return 'water'; } if (name.includes('steam') || name.includes('пар')) { return 'steam'; } if (name.includes('co2') || name.includes('углекислый') || cat.includes('emission')) { return 'CO2'; } if (name.includes('waste') || name.includes('отход') || cat.includes('waste')) { return 'biowaste'; } if (name.includes('cool') || name.includes('охлажден') || cat.includes('cooling')) { return 'cooling'; } if (name.includes('logistic') || name.includes('логистик') || cat.includes('logistics')) { return 'logistics'; } if (name.includes('material') || name.includes('материал') || cat.includes('material')) { return 'materials'; } if (name.includes('service') || name.includes('услуг') || cat.includes('service')) { return 'service'; } // Default fallback return 'materials'; } /** * Parse quantity string to amount and unit * Examples: "100 kg", "50 m3/hour", "200" */ function parseQuantity(quantityStr?: string): { amount: number; unit: string; temporal_unit?: string; } { if (!quantityStr || !quantityStr.trim()) { return { amount: 0, unit: 'unit' }; } // Try to parse "amount unit" or "amount unit/temporal_unit" const parts = quantityStr.trim().split(/\s+/); const amount = parseFloat(parts[0]) || 0; if (parts.length >= 2) { const unitPart = parts[1]; if (unitPart.includes('/')) { const [unit, temporal_unit] = unitPart.split('/'); return { amount, unit, temporal_unit }; } return { amount, unit: unitPart }; } return { amount, unit: 'unit' }; } /** * Convert user-friendly needs/offers to ResourceFlow requests */ export function convertNeedsAndOffersToResourceFlows( formData: OrganizationFormData, organizationId: string, siteId: string ): CreateResourceFlowRequest[] { const flows: CreateResourceFlowRequest[] = []; // Convert needs (direction='input') if (formData.needs && formData.needs.length > 0) { for (const need of formData.needs) { const resourceType = inferResourceType(need.resource_name, need.category); const quantity = parseQuantity(need.quantity); // Validate resource type const validatedType = resourceTypeSchema.safeParse(resourceType); const finalType = validatedType.success ? validatedType.data : 'materials'; if (!validatedType.success) { console.warn(`Invalid resource type "${resourceType}", defaulting to "materials"`); } flows.push({ organization_id: organizationId, site_id: siteId, direction: 'input', type: finalType, quantity: { amount: quantity.amount, unit: quantity.unit, temporal_unit: quantity.temporal_unit, }, // Optional fields can be added later if needed precision_level: 'declared', // User-entered data is "declared" source_type: 'declared', }); } } // Convert offers (direction='output') if (formData.offers && formData.offers.length > 0) { for (const offer of formData.offers) { const resourceType = inferResourceType(offer.resource_name, offer.category); const quantity = parseQuantity(offer.quantity); // Validate resource type const validatedType = resourceTypeSchema.safeParse(resourceType); const finalType = validatedType.success ? validatedType.data : 'materials'; if (!validatedType.success) { console.warn(`Invalid resource type "${resourceType}", defaulting to "materials"`); } flows.push({ organization_id: organizationId, site_id: siteId, direction: 'output', type: finalType, quantity: { amount: quantity.amount, unit: quantity.unit, temporal_unit: quantity.temporal_unit, }, // Optional fields can be added later if needed precision_level: 'declared', // User-entered data is "declared" source_type: 'declared', }); } } return flows; }