mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- Remove nested git repository from bugulma/frontend/.git - Add all frontend files to main repository tracking - Convert from separate frontend/backend repos to unified monorepo - Preserve all frontend code and development history as tracked files - Eliminate nested repository complexity for simpler development workflow This creates a proper monorepo structure with frontend and backend coexisting in the same repository for easier development and deployment.
152 lines
4.9 KiB
TypeScript
152 lines
4.9 KiB
TypeScript
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;
|
|
}
|