Agent 2 - Database Integration (P0 Critical): - Add Prisma ORM with PostgreSQL for persistent data storage - Create comprehensive database schema with 20+ models: - User & authentication models - Plant & lineage tracking - Transport events & supply chain - Vertical farming (farms, zones, batches, recipes) - Demand & market matching - Audit logging & blockchain storage - Implement complete database service layer (lib/db/): - users.ts: User CRUD with search and stats - plants.ts: Plant operations with lineage tracking - transport.ts: Transport events and carbon tracking - farms.ts: Vertical farm and crop batch management - demand.ts: Consumer preferences and market matching - audit.ts: Audit logging and blockchain integrity - Add PlantChainDB for database-backed blockchain - Create development seed script with sample data - Add database documentation (docs/DATABASE.md) - Update package.json with Prisma dependencies and scripts This provides the foundation for all other agents to build upon with persistent, scalable data storage.
150 lines
3.3 KiB
TypeScript
150 lines
3.3 KiB
TypeScript
/**
|
|
* Database Types and Utilities
|
|
* Type helpers for working with Prisma and the database
|
|
*/
|
|
|
|
import type { Prisma } from '@prisma/client';
|
|
|
|
// Re-export Prisma types for convenience
|
|
export type {
|
|
User,
|
|
Plant,
|
|
TransportEvent,
|
|
VerticalFarm,
|
|
GrowingZone,
|
|
CropBatch,
|
|
GrowingRecipe,
|
|
SeedBatch,
|
|
HarvestBatch,
|
|
ConsumerPreference,
|
|
DemandSignal,
|
|
SupplyCommitment,
|
|
MarketMatch,
|
|
SeasonalPlan,
|
|
DemandForecast,
|
|
PlantingRecommendation,
|
|
ResourceUsage,
|
|
FarmAnalytics,
|
|
AuditLog,
|
|
BlockchainBlock,
|
|
} from '@prisma/client';
|
|
|
|
// Common query options
|
|
export interface PaginationOptions {
|
|
page?: number;
|
|
limit?: number;
|
|
cursor?: string;
|
|
}
|
|
|
|
export interface SortOptions {
|
|
field: string;
|
|
direction: 'asc' | 'desc';
|
|
}
|
|
|
|
// Location-based query options
|
|
export interface LocationFilter {
|
|
latitude: number;
|
|
longitude: number;
|
|
radiusKm: number;
|
|
}
|
|
|
|
// Date range filter
|
|
export interface DateRangeFilter {
|
|
start: Date;
|
|
end: Date;
|
|
}
|
|
|
|
// Create input types for common operations
|
|
export type CreateUserInput = Prisma.UserCreateInput;
|
|
export type UpdateUserInput = Prisma.UserUpdateInput;
|
|
|
|
export type CreatePlantInput = Prisma.PlantCreateInput;
|
|
export type UpdatePlantInput = Prisma.PlantUpdateInput;
|
|
|
|
export type CreateTransportEventInput = Prisma.TransportEventCreateInput;
|
|
export type UpdateTransportEventInput = Prisma.TransportEventUpdateInput;
|
|
|
|
export type CreateVerticalFarmInput = Prisma.VerticalFarmCreateInput;
|
|
export type UpdateVerticalFarmInput = Prisma.VerticalFarmUpdateInput;
|
|
|
|
export type CreateCropBatchInput = Prisma.CropBatchCreateInput;
|
|
export type UpdateCropBatchInput = Prisma.CropBatchUpdateInput;
|
|
|
|
// Result types with includes
|
|
export type PlantWithOwner = Prisma.PlantGetPayload<{
|
|
include: { owner: true };
|
|
}>;
|
|
|
|
export type PlantWithLineage = Prisma.PlantGetPayload<{
|
|
include: {
|
|
owner: true;
|
|
parentPlant: true;
|
|
childPlants: true;
|
|
};
|
|
}>;
|
|
|
|
export type TransportEventWithParties = Prisma.TransportEventGetPayload<{
|
|
include: {
|
|
sender: true;
|
|
receiver: true;
|
|
plants: true;
|
|
};
|
|
}>;
|
|
|
|
export type VerticalFarmWithZones = Prisma.VerticalFarmGetPayload<{
|
|
include: {
|
|
owner: true;
|
|
zones: true;
|
|
cropBatches: true;
|
|
};
|
|
}>;
|
|
|
|
// Utility type for pagination results
|
|
export interface PaginatedResult<T> {
|
|
items: T[];
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
totalPages: number;
|
|
hasMore: boolean;
|
|
}
|
|
|
|
// Helper function to calculate distance between two points (Haversine formula)
|
|
export function calculateDistanceKm(
|
|
lat1: number,
|
|
lon1: number,
|
|
lat2: number,
|
|
lon2: number
|
|
): number {
|
|
const R = 6371; // Earth's radius in km
|
|
const dLat = toRad(lat2 - lat1);
|
|
const dLon = toRad(lon2 - lon1);
|
|
const a =
|
|
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
|
|
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
return R * c;
|
|
}
|
|
|
|
function toRad(deg: number): number {
|
|
return deg * (Math.PI / 180);
|
|
}
|
|
|
|
// Helper to create pagination result
|
|
export function createPaginatedResult<T>(
|
|
items: T[],
|
|
total: number,
|
|
page: number,
|
|
limit: number
|
|
): PaginatedResult<T> {
|
|
const totalPages = Math.ceil(total / limit);
|
|
return {
|
|
items,
|
|
total,
|
|
page,
|
|
limit,
|
|
totalPages,
|
|
hasMore: page < totalPages,
|
|
};
|
|
}
|