turash/bugulma/frontend/schemas/backend/match.ts
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- 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.
2025-11-25 06:02:57 +01:00

170 lines
6.2 KiB
TypeScript

import { z } from 'zod';
import {
createBackendEntitySchema,
idSchema,
timestampSchema,
nonNegativeNumberSchema,
positiveNumberSchema,
optionalUrlSchema,
scoreSchema,
} from '@/schemas/common';
/**
* Backend-aligned Match schemas
* Matches the Go backend domain.Match struct and related types
*
* Uses Zod v4's composition features and common schemas for DRY code
*/
/**
* Match status enum
* Uses Zod v4's improved enum support
*/
export const matchStatusSchema = z
.enum(['suggested', 'negotiating', 'reserved', 'contracted', 'live', 'failed', 'cancelled'])
.describe('Match status');
export type MatchStatus = z.infer<typeof matchStatusSchema>;
/**
* Transportation estimate schema
* Uses Zod v4's composition with common schemas
*/
export const transportationEstimateSchema = z
.object({
cost_per_year: nonNegativeNumberSchema.describe('Annual transportation cost'),
method: z.string().describe('Transportation method (piping, truck, tanker)'),
feasibility_score: scoreSchema.describe('Feasibility score (0-1)'),
})
.describe('Transportation estimate');
export type TransportationEstimate = z.infer<typeof transportationEstimateSchema>;
/**
* Risk assessment schema
* Uses Zod v4's composition with score validation
*/
export const riskAssessmentSchema = z
.object({
technical_risk: scoreSchema.describe('Technical risk (0-1)'),
regulatory_risk: scoreSchema.describe('Regulatory risk (0-1)'),
market_risk: scoreSchema.optional().describe('Market risk (0-1)'),
counterparty_risk: scoreSchema.optional().describe('Counterparty risk (0-1)'),
})
.describe('Risk assessment');
export type RiskAssessment = z.infer<typeof riskAssessmentSchema>;
/**
* Economic impact schema
* Uses Zod v4's composition with common number schemas
*/
export const economicImpactSchema = z
.object({
annual_savings: nonNegativeNumberSchema.optional().describe('Annual savings (€/year)'),
npv: z.number().optional().describe('Net Present Value'),
irr: z.number().optional().describe('Internal Rate of Return (%)'),
payback_years: positiveNumberSchema.optional().describe('Payback period (years)'),
co2_avoided_tonnes: nonNegativeNumberSchema.optional().describe('CO2 avoided (tonnes/year)'),
capex_required: nonNegativeNumberSchema.optional().describe('Initial investment (CAPEX)'),
opex_per_year: nonNegativeNumberSchema.optional().describe('Annual operational cost (OPEX)'),
})
.describe('Economic impact analysis');
export type EconomicImpact = z.infer<typeof economicImpactSchema>;
/**
* Match history entry schema
* Uses Zod v4's composition with timestamp validation
*/
export const matchHistoryEntrySchema = z
.object({
timestamp: timestampSchema.describe('Entry timestamp'),
actor: idSchema.describe('User ID who performed the action'),
action: z.string().describe('Action type (status_change, comment, update)'),
notes: z.string().optional().describe('Additional notes'),
old_value: z.string().optional().describe('Previous value'),
new_value: z.string().optional().describe('New value'),
})
.describe('Match history entry');
export type MatchHistoryEntry = z.infer<typeof matchHistoryEntrySchema>;
/**
* Contract details schema
* Uses Zod v4's composition with timestamp and URL validation
*/
export const contractDetailsSchema = z
.object({
contract_id: idSchema.optional().describe('Contract identifier'),
signed_at: timestampSchema.optional().describe('Contract signing timestamp'),
effective_from: timestampSchema.optional().describe('Contract effective date'),
value_per_year: nonNegativeNumberSchema.optional().describe('Annual contract value'),
terms_url: optionalUrlSchema.describe('URL to contract terms'),
})
.describe('Contract details');
export type ContractDetails = z.infer<typeof contractDetailsSchema>;
/**
* Backend Match schema
* Uses Zod v4's composition with common entity schema
*/
export const backendMatchSchema = createBackendEntitySchema({
SourceResourceID: idSchema.describe('Source resource flow ID'),
TargetResourceID: idSchema.describe('Target resource flow ID'),
CompatibilityScore: scoreSchema.describe('Overall compatibility score (0-1)'),
TemporalOverlapScore: scoreSchema.optional().describe('Temporal overlap score (0-1)'),
QualityScore: scoreSchema.optional().describe('Quality compatibility score (0-1)'),
EconomicValue: z.number().describe('Economic value'),
DistanceKm: nonNegativeNumberSchema.describe('Distance in kilometers'),
Status: matchStatusSchema,
Priority: z.number().int().describe('Match priority'),
TransportationEstimate: transportationEstimateSchema.optional(),
RiskAssessment: riskAssessmentSchema.optional(),
EconomicImpact: economicImpactSchema.optional(),
History: z.array(matchHistoryEntrySchema).optional().describe('Match history'),
ContractDetails: contractDetailsSchema.optional(),
ReservedUntil: timestampSchema.optional().describe('Reservation expiration timestamp'),
FailureReason: z.string().optional().describe('Reason for failure (if failed)'),
Version: z.number().int().optional().describe('Optimistic locking version'),
});
export type BackendMatch = z.infer<typeof backendMatchSchema>;
/**
* Response schema for find matches endpoint
* Uses Zod v4's composition
*/
export const findMatchesResponseSchema = z.object({
resource_id: idSchema.describe('Resource flow ID (from handler)'),
matches: z.array(backendMatchSchema).describe('Matching resource flows'),
count: z.number().int().nonnegative().describe('Total match count'),
});
export type FindMatchesResponse = z.infer<typeof findMatchesResponseSchema>;
/**
* Query parameters for finding matches
* Uses Zod v4's coerce and common number schemas
*/
export const findMatchesQuerySchema = z.object({
max_distance_km: z.coerce
.number()
.pipe(positiveNumberSchema)
.optional()
.describe('Maximum distance in kilometers'),
min_score: z.coerce
.number()
.pipe(scoreSchema)
.optional()
.describe('Minimum compatibility score (0-1)'),
limit: z.coerce
.number()
.int()
.pipe(positiveNumberSchema)
.optional()
.describe('Maximum number of results'),
});
export type FindMatchesQuery = z.infer<typeof findMatchesQuerySchema>;